前提(此文档):有一个能跑通的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文档,例如:
4.2 在刚刚打开的word文档里面把需要替换的内容用占位符进行替换,如下:
4.3 将word文档另存为xxx.xml格式,如下:
4.4 将保存的xml文件复制到你项目的静态资源目录下并将后缀名改为ftl,如下:
这样,你就完成了模板准备的任务。
最后,测试:①读取模板-->②启动项目-->③浏览器访问接口
关键操作如下
/**
* 构造函数
* 配置模板路径
* @param encoding
*/
public ExportWord(String encoding) {
this.encoding = encoding;
configuration = new Configuration();
configuration.setDefaultEncoding(encoding);
configuration.setClassForTemplateLoading(this.getClass(), "/templates");
}
这里最后面那个"/templates"必须和下图对的上
然后是这里要和你的模板名称对的上
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要为你的模板名称
最后,把项目跑起来,由浏览器访问接口,本人亲测效果如下:
注意,占位符里面声明的字段必须在你所声明的map里面存在(如果不存在的话会报错)。
ps: 同时注意关于占位符里面字段的非空判断,非空判断可以在模板里完成也可以在代码里完成。(没做好这块工作也容易报错)
关于wordXML格式解析,你可以参考这篇文档:https://www.cnblogs.com/klbc/p/4005799.html