1. 程式人生 > >使用 freemark 實現 報表匯出

使用 freemark 實現 報表匯出

使用 freemark 實現 報表匯出

1 準備 excel 模板

  • 1.1 建立 excel 並且輸入一點測試資料,然後以 xml 格式另存為

  • 1.2 開啟 xml 找到


前後省略 n 多東西
..... 

<Table ss:ExpandedColumnCount="2" ss:ExpandedRowCount="2" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight
="13.5">
<Row> <Cell> <Data ss:Type="String">標題1</Data> </Cell> </Row> <Row> <Cell> <Data ss:Type="String">內容1</Data> </Cell> </Row> </Table> .....
  • 1.3 用 freemark 的語法改造後的樣子
<Table ss:ExpandedColumnCount="2" ss:ExpandedRowCount="2" x:FullColumns="1" x:FullRows="1" ss:DefaultColumnWidth="54" ss:DefaultRowHeight="13.5">
    <Row>
        <Cell>
            <Data ss:Type="String">標題1</Data>
        </Cell>
    </Row>
    <Row>
        <#list datas as data>
            <Cell>
                <Data ss:Type="String">${data.get("xxx")!''}</Data>
            </Cell>
        </#list>
    </Row>
</Table>

2 Controller 寫法

  • 2.1 將查詢的來的list 用hashMap封裝一下
    List datalist = 自己的業務邏輯查出來的 list
    Map<String, Object> resultMap = new HashMap<>(1);
    resultMap.put("datas", datalist);
    String fileName = "xx表.xls";
    new ReportExportHelper().doExport(resultMap, fileName, exportReportConfig, overdueTemplateName, response);
  • 2.2 當 Controller 呼叫 ReportExportHelper 的 doExport 就完成報表匯出了, 你沒看錯就這麼簡單。

3 相關工具類

3.0 ExportReportConfig

package cn.istarfinancial.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

/**
 * 報表匯出配置類
 *
 * @Author: huangwenjun
 * @Description:
 * @Date: Created in 11:25  2018/4/14
 **/
@Component
@ConfigurationProperties(prefix = "report")
public class ExportReportConfig {

    /**
     * 模板根目錄
     */
    private String templateRootPath;

    /**
     * 用於存放臨時檔案的目錄
     */
    private String tempReport;

    public String getTemplateRootPath() {
        return templateRootPath;
    }

    public void setTemplateRootPath(String templateRootPath) {
        this.templateRootPath = templateRootPath;
    }

    public String getTempReport() {
        return tempReport;
    }

    public void setTempReport(String tempReport) {
        this.tempReport = tempReport;
    }
}

3.1 ReportExportHelper

package cn.istarfinancial.utils;

import cn.istarfinancial.config.ExportReportConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.Map;

/**
 * 報表匯出幫助者
 *
 * @Author: huangwenjun
 * @Description:
 * @Date: Created in 14:59  2018/4/14
 **/
public class ReportExportHelper {

    private Logger logger = LoggerFactory.getLogger(ReportExportHelper.class);

    /**
     * 執行報表匯出
     *
     * 匯出流程 1 查詢資料,然後再伺服器上生成報表
     *          2 將包表寫回
     *
     * @param resultMap
     * @param fileName
     */
    public void doExport(Map<String, Object> resultMap, String fileName, ExportReportConfig exportReportConfig, String templateName, HttpServletResponse response) {
        try {
            boolean success = TemplateParseUtil.parse(exportReportConfig.getTemplateRootPath(), templateName, exportReportConfig.getTempReport() + "//" + fileName, resultMap);

            if (success) {
                // 模板生成成功
                boolean downloaded = FileTransUtil.download(response, fileName, exportReportConfig.getTempReport() + "\\" + fileName);

                if (downloaded) {
                    new File(exportReportConfig.getTempReport() + "//" + fileName).delete();
                }
            }
        } catch (Exception e) {
            logger.error("模板匯出出錯");
        }
    }
}

3.2 TemplateParseUtil

package cn.istarfinancial.utils;

import freemarker.template.TemplateException;

/**
 * 模板解析實體類
 */
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import freemarker.cache.StringTemplateLoader;
import freemarker.template.Configuration;
import freemarker.template.DefaultObjectWrapper;
import freemarker.template.Template;

/**
 * @Author: huangwenjun
 * @Description:
 * @Date: Created in 9:30  2018/4/14
 **/
public class TemplateParseUtil {

    /**
     * 解析模板生成Excel
     *
     * @param templateDir  模板目錄
     * @param templateName 模板名稱
     * @param excelPath    生成的Excel檔案路徑
     * @param data         資料引數
     * @throws IOException
     * @throws TemplateException
     */
    public static boolean parse(String templateDir, String templateName, String excelPath, Map<String, Object> data) throws IOException, TemplateException {
        //初始化工作
        Configuration cfg = new Configuration();
        //設定預設編碼格式為UTF-8
        cfg.setDefaultEncoding("UTF-8");
        //全域性數字格式
        cfg.setNumberFormat("0.00");
        //設定模板檔案位置
        cfg.setDirectoryForTemplateLoading(new File(templateDir));
        cfg.setObjectWrapper(new DefaultObjectWrapper());
        //載入模板
        Template template = cfg.getTemplate(templateName, "utf-8");
        try (OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream(excelPath), "UTF-8")) {
            //填充資料至Excel
            template.process(data, writer);
            writer.flush();
        }

        return true;
    }

    public static void main(String[] args) throws IOException, TemplateException {
        List<String> str = new ArrayList<>();

        for(int i = 1 ; i <= 3;i ++){
            str.add("str:" + i);
        }

        //測試Excel檔案生成
        Map<String,Object> data = new HashMap<>();
        data.put("strs", str);

        String templatePath = "E:\\idea_project\\xingrong-report\\src\\main\\resources\\templates";

        TemplateParseUtil.parse(templatePath, "overdue_template.ftl", templatePath + "/xmlTest.xls", data);
    }
}

3.3 FileTransUtil

package cn.istarfinancial.utils;

import javax.servlet.http.HttpServletResponse;
import java.io.*;

/**
 * 檔案傳輸工具類
 *
 * @Author: huangwenjun
 * @Description:
 * @Date: Created in 10:25  2018/4/14
 **/
public class FileTransUtil {

    /**
     * 檔案下載
     * @param response
     * @param fileName
     * @param filePath
     * @return 是否下載成功
     */
    public static boolean download(HttpServletResponse response, String fileName, String filePath) throws IOException {
        response.setHeader("content-type", "application/octet-stream");
        response.setContentType("application/octet-stream;charset=utf-8");
        // 解決下載中文檔名的bug
        fileName = new String(fileName.getBytes(), "ISO-8859-1");
        response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
        byte[] buff = new byte[1024];

        try (OutputStream os = response.getOutputStream();
            BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File(filePath)))) {
            int len = 0;
            while ((len = bis.read(buff)) > 0) {
                os.write(buff, 0, len);
            }
        } catch (IOException e) {
            throw e;
        }

        return true;
    }
}

4 坑 & 解決方案

上述解決方案在 wps 能 完美執行, 但是在低版本的 office 裡面 無法正常開啟, 會啟動報錯。

4.1 無腦快速解決方案

  • 1 打卡 xml 設定 ExpandedColumnCount=”999999” 和 ExpandedRowCount=”999999”

4.2 正兒八經的排查錯誤

 一般開啟錯誤的話會 office 生成日誌,而且告訴你問題所在

 樓主的錯誤日誌路徑位於:

 C:\Users\Administrator\AppData\Local\Microsoft\Windows\INetCache\Content.MSO

 有些電腦可能找不到這個路徑 需要設定兩個地方
 1 設定電腦 顯示隱藏資料夾
 2 取消勾選 隱藏受保護的作業系統檔案

 然後你就能看到各種錯誤日誌了。。。

5 寫在最後

最後還是有一個小問題無法解決, 就是低版本的 office 打卡會報一個警告。。。有解決方案的兄弟 麻煩告訴我一聲