使用freemarker模板引擎导出word文档(springBoot)

前提(此文档):有一个能跑通的springBoot项目

1.在你的pom.xml文件中添加以下依赖:


    org.freemarker
    freemarker
    2.3.23

2.编写相关工具类,工具类代码如下:

package com.tefei.isak.common.utils;

import com.sun.deploy.net.URLEncoder;
import freemarker.template.Configuration;
import freemarker.template.Template;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.Map;

/**
 * @author linyh
 * @email [email protected]
 * @date 2020/7/27 14:55
 */
public class ExportWord {

    private static Logger logger = LoggerFactory.getLogger(ExportWord.class);
    private Configuration configuration;
    private String encoding;
    private String exportPath = "D:\\test\\";

    /**
     * 构造函数
     * 配置模板路径
     * @param encoding    
     */
    public ExportWord(String encoding) {
        this.encoding = encoding;
        configuration = new Configuration();
        configuration.setDefaultEncoding(encoding);
        configuration.setClassForTemplateLoading(this.getClass(), "/templates");
    }

    /**
     * 获取模板
     * @param name
     * @return
     * @throws Exception    
     */
    public Template getTemplate(String name) throws Exception {
        return configuration.getTemplate(name);
    }

    /**
     * 导出word文档到指定目录
     * @param fileName
     * @param tplName
     * @param data
     * @throws Exception    
     */
    public void exportDocFile(String fileName, String tplName, Map data) throws Exception {
        logger.debug("导出word到D:\test");
        //如果目录不存在,则创建目录
        File exportDirs = new File(exportPath);
        if (!exportDirs.exists()) {
            exportDirs.mkdirs();
        }
        Writer writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(exportPath + fileName), encoding));
        getTemplate(tplName).process(data, writer);
    }

    /**
     * 导出word文档到客户端
     * @param response
     * @param fileName
     * @param tplName
     * @param data
     * @throws Exception    
     */
    public void exportDoc(HttpServletResponse response, String fileName, String tplName, Map data) throws Exception {
        response.reset();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/msword");
        response.setHeader("Content-Disposition", "attachment; filename=" +  URLEncoder.encode(fileName , "UTF-8"));
        // 把本地文件发送给客户端
        Writer out = response.getWriter();
        Template template = getTemplate(tplName);
        template.process(data, out);
        out.close();
    }
}

3.然后我们只需要去编写一个controller去调用工具类里面的方法即可(我这里是调用的是exportDoc方法):

package com.tefei.isak.controller.exportword;

/**
 * @author linyh
 * @email [email protected]
 * @date 2020/7/27 15:09
 */
import com.tefei.isak.common.utils.ExportWord;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/export")
public class ExportDocController {
    @RequestMapping(value = "/exportWord", method= RequestMethod.GET)
    public void exportWord(HttpServletRequest request, HttpServletResponse response) throws Exception {
            String fileName = "江西省政协委员履职信息登记反馈系统-测试报告.doc"; //文件名称
            Map dataMap = new HashMap<>();
            dataMap.put("code","R20000"); //报告编号
            dataMap.put("productName","江西省政协委员履职信息登记反馈系统"); //产品名称
            dataMap.put("version","V1.0"); //产品版本
            dataMap.put("testType","验收测试"); //测试版本
            dataMap.put("devUnit","拓维信息系统股份有限公司"); //开发单位
            dataMap.put("postUnit","江西省政协"); //送测单位
            dataMap.put("date","2019年8月6日"); //签发日期
            dataMap.put("address","南昌市福州路235号"); //地址
            dataMap.put("postCode","330019"); //邮政编码
            dataMap.put("phone","0791-86310829  0791-86239291"); //电话
            dataMap.put("fax","0791-86310829"); //传真
            dataMap.put("email","[email protected]"); //邮箱
            dataMap.put("postAddress","江西省南昌市红谷滩新区红角洲卧龙路999号"); //送测单位地址
            dataMap.put("productType","应用软件"); //产品类别
            dataMap.put("startDate","2019.7.20"); //到样日期
            dataMap.put("testDate","2019.7.21~2019.8.5"); //测试日期
            dataMap.put("content1","光盘(/)"); //样品内容和数量
            dataMap.put("content2","用户手册(1份)"); //样品内容和数量
            dataMap.put("address1","现场"); //测试地点
            dataMap.put("address2","在线"); //测试地点
            dataMap.put("address3","本单位"); //测试地点
            dataMap.put("testRely1","(1)GB/T 25000.10-2016《系统与软件工程 系统与软件质量要求和评价(SQuaRE)第10部分:系统与软件质量模型》"); //测试依据1
            dataMap.put("testRely2","(2)GB/T 25000.51-2016《系统与软件工程 系统与软件质量要求和评价(SQuaRE)第51部分:就绪可用软件产品(RUSP)的质量要求和测试细则》"); //测试依据2
            dataMap.put("testRely3","(3)GB/T 16260.2-2006 《软件工程 产品质量 第2部分:外部度量》"); //测试依据3
            dataMap.put("judgeRely","《江西省政协委员履职信息登记反馈系统用户手册》"); //判定依据
            dataMap.put("testResult","经本中心测试,该软件在功能性、性能、可靠性、安全性、易用性、兼容性、维护性、可移植性、用户文档、用户界面等方面符合要求,测试通过。");
            dataMap.put("testSign","纪芩"); //测试签名
            dataMap.put("auditeSign","公磊"); //审核签名
            dataMap.put("approveSign","万鹏"); //批准签名
            dataMap.put("","");
            new ExportWord("UTF-8").exportDoc(response, fileName, "test.ftl", dataMap);
    }
}

说明:其实这里可以把controller层的具体实现放到一个service里面,然后再用controller去调用service,这样的话更符合"各自分工"的标准,controller只负责接收页面请求,service负责后台逻辑。(偷懒了= - =)
4.然后就是关于如何准备自己的模板(很简单的,不要怂)

步骤如下:

4.1 随便打开一个word文档,例如:


aa.jpg

4.2 在刚刚打开的word文档里面把需要替换的内容用占位符进行替换,如下:


cc.jpg

4.3 将word文档另存为xxx.xml格式,如下:


bb.jpg

4.4 将保存的xml文件复制到你项目的静态资源目录下并将后缀名改为ftl,如下:


dd.jpg
ee.jpg

这样,你就完成了模板准备的任务。

最后,测试:①读取模板-->②启动项目-->③浏览器访问接口

关键操作如下

 /**
     * 构造函数
     * 配置模板路径
     * @param encoding    
     */
    public ExportWord(String encoding) {
        this.encoding = encoding;
        configuration = new Configuration();
        configuration.setDefaultEncoding(encoding);
        configuration.setClassForTemplateLoading(this.getClass(), "/templates");
    }

这里最后面那个"/templates"必须和下图对的上


ff.jpg

然后是这里要和你的模板名称对的上

package com.tefei.isak.controller.exportword;

/**
 * @author linyh
 * @email [email protected]
 * @date 2020/7/27 15:09
 */
import com.tefei.isak.common.utils.ExportWord;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/export")
public class ExportDocController {
    @RequestMapping(value = "/exportWord", method= RequestMethod.GET)
    public void exportWord(HttpServletRequest request, HttpServletResponse response) throws Exception {
            String fileName = "江西省政协委员履职信息登记反馈系统-测试报告.doc"; //文件名称
            Map dataMap = new HashMap<>();
            new ExportWord("UTF-8").exportDoc(response, fileName, "aa.ftl", dataMap);
    }
}

即代码里的那个aa.ftl要为你的模板名称
最后,把项目跑起来,由浏览器访问接口,本人亲测效果如下:


gg.jpg
hh.jpg

注意,占位符里面声明的字段必须在你所声明的map里面存在(如果不存在的话会报错)。

ps: 同时注意关于占位符里面字段的非空判断,非空判断可以在模板里完成也可以在代码里完成。(没做好这块工作也容易报错)

关于wordXML格式解析,你可以参考这篇文档:https://www.cnblogs.com/klbc/p/4005799.html

你可能感兴趣的:(使用freemarker模板引擎导出word文档(springBoot))