使用 freemark 实现 报表导出

使用 freemark 实现 office 文档生成

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>
  • 1.4 项目中导入 freemark 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-freemarker</artifactId>
</dependency>
  • 1.5 新增配置
report.templateRootPath=F:\\idea_workspace\\framework\\ga-data-open-service\\src\\main\\resources\\template
report.tempReport=G:\\temp
report.templateName=docTemplate.ftl

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, response);
  • 2.2 当 Controller 调用 ReportExportHelper 的 doExport 就完成报表导出了, 你没看错就这么简单。

3 相关工具类

3.0 ExportReportConfig

package com.surfilter.business.dataservice.config;

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;

/**
 * @description:
 * @author: huangwenjun
 * @createDate: 2020/1/8
 */
@Configuration
@ConfigurationProperties(prefix = "report")
@Data
public class ExportReportConfig {

    /**
     * 模板目录
     */
    private String templateRootPath;

    /**
     * 用于存放临时文件的目录
     */
    private String tempReport;

    /**
     * 模板文件名
     */
    private String templateName;
}


3.1 ReportExportHelper

package com.surfilter.business.dataservice.util;

import com.surfilter.business.dataservice.config.ExportReportConfig;
import lombok.extern.slf4j.Slf4j;

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

/**
 * @description:
 * @author: huangwenjun
 * @createDate: 2020/1/8
 */
@Slf4j
public class ReportExportHelper {

    /**
     * 执行报表导出
     *
     * 导出流程 1 查询数据,然后再服务器上生成报表
     *          2 将包表写回
     *
     * @param resultMap
     * @param fileName
     */
    public static void doExport(Map<String, Object> resultMap, String fileName, ExportReportConfig exportReportConfig, HttpServletResponse response) {
        try {
            boolean success = TemplateParseUtil.parse(exportReportConfig.getTemplateRootPath(), exportReportConfig.getTemplateName(), exportReportConfig.getTempReport() + File.separator + fileName, resultMap);

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

                if (downloaded) {
                    new File(exportReportConfig.getTempReport() + File.separator + fileName).delete();
                }
            }
        } catch (Exception e) {
            log.error("模板导出出错:", e);
        }
    }
}

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;
	}
}

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 打卡会报一个警告。。。有解决方案的兄弟 麻烦告诉我一声

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值