借助freemarker实现word模板复制功能
程序员文章站
2022-04-30 09:25:22
...
业务场景
导出所有报名用户的报名表,每个用户报名单在word文档中单独一页,方便打印。
报名单模板如下:
需求:一份word文档,包含所有用户的报名单,每个用户单独一页。
实现思路
- 将报名单内容设置成模板,其中需求提到每个用户要单独一页,所以在模板的后面加上换页效果
- 一份word文档里面要包含多个用户报名信息,所以模板要重复使用,只是替换用户信息
- 将word模板转成xml文件,获取内容信息的模板格式
- 将xml转成ftl格式,支持el表达式,支持循环,很好的实现需求
具体实现
- 用工具(word/wps)创建好模板的word文档
- 将word文档另存为XML文件,命名为apply.xml,打开xml文件,如果是压缩后的XML内容,直接在网上将其格式化,方便阅读和修改
- 将XML中需要替换的内容使用EL表达式代替,如:${name}。PS:可以在创建模板时直接使用EL表达式,但是注意在转换成XML的时候可能会存在某些意外,如果不熟悉使用。
- 业务需要多个报名单,每份报名单格式都是一样的,所以在XML内容的一开始就加上循环遍历的代码:<#list recordList as record>…..
部分XML内容
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<?mso-application progid="Word.Document"?>
<w:wordDocument xmlns:aml="http://schemas.microsoft.com/aml/2001/core"
xmlns:dt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"
xmlns:o="urn:schemas-microsoft-com:office:office"
xmlns:v="urn:schemas-microsoft-com:vml"
xmlns:w10="urn:schemas-microsoft-com:office:word"
xmlns:w="http://schemas.microsoft.com/office/word/2003/wordml"
xmlns:wx="http://schemas.microsoft.com/office/word/2003/auxHint"
xmlns:wsp="http://schemas.microsoft.com/office/word/2003/wordml/sp2"
xmlns:sl="http://schemas.microsoft.com/schemaLibrary/2003/core"
w:macrosPresent="no" w:embeddedObjPresent="no" w:ocxPresent="no"
xml:space="preserve">
<w:body>
<#list recordList as record>
...... 此处省略部分内容
<w:t>姓名</w:t>
...... 此处省略部分内容
<w:t>${record.name}</w:t>
...... 此处省略部分内容
</#list>
</w:body>
</w:wordDocument>
JAVA代码
控制器代码:
/**
* 导出报名列表
*
* @param request
* @param response
* @throws IOException
*/
@RequestMapping(value = "/manage/zsh/apply/record/export-word")
public void exportWord(HttpServletRequest request, HttpServletResponse response) throws IOException {
request.setCharacterEncoding("UTF-8");
String path = "";
String fileName = new String("报名表.doc".getBytes("UTF-8"), "iso-8859-1");
path = buildWordUrl(request, response);
response.setCharacterEncoding("utf-8");
response.setContentType("application/msword");
response.setHeader("Content-Disposition", "attachment; filename=" + fileName);
File file = new File(path);
InputStream inputStream = new FileInputStream(file);
OutputStream os = response.getOutputStream();
byte[] b = new byte[10240];
int length;
while ((length = inputStream.read(b)) > 0) {
os.write(b, 0, length);
}
inputStream.close();
os.flush();
os.close();
file.delete();
}
/**
* word文档地址
*
* @param request
* @param response
* @return
*/
private String buildWordUrl(HttpServletRequest request, HttpServletResponse response) {
String outDocPath = request.getSession().getServletContext().getRealPath("/") + "/download/"
+ System.currentTimeMillis() + ".doc";
FreemarkerToWord freemarkerToWord = new FreemarkerToWord();
freemarkerToWord.createWord(request, outDocPath, "/template", "apply.min.ftl");
return outDocPath;
}
核心代码:
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import freemarker.core.ParseException;
import freemarker.template.Configuration;
import freemarker.template.MalformedTemplateNameException;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import freemarker.template.TemplateNotFoundException;
/**
* 借助freemarker构建word文档
*
* @author 黄国渝
* @version 1.0.0
*/
public class FreemarkerToWord {
private Configuration configuration = null;
public FreemarkerToWord() {
configuration = new Configuration(Configuration.VERSION_2_3_23);
configuration.setDefaultEncoding("UTF-8");
}
public void createWord(HttpServletRequest request, String outDocPath, String templatePath, String tmplateName) {
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("recordList", getData());
// 模板获取方式有很多种,因为我的模板放在部署中间件的WebContent中,所以采用以下方式
configuration.setServletContextForTemplateLoading(request.getSession().getServletContext(), templatePath);
try {
// 注释的内容是旧的写法,会导致乱码
// configuration.getTemplate(tmplateName);
configuration.getTemplate(tmplateName, "UTF-8");
Template t = configuration.getTemplate(tmplateName);
File outFile = new File(outDocPath);
// 注释的内容是旧的写法,会导致乱码
// Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile)));
Writer out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(outFile), "UTF-8"));
t.process(dataMap, out);
} catch (TemplateNotFoundException e2) {
e2.printStackTrace();
} catch (MalformedTemplateNameException e2) {
e2.printStackTrace();
} catch (ParseException e2) {
e2.printStackTrace();
} catch (IOException e2) {
e2.printStackTrace();
} catch (TemplateException e) {
e.printStackTrace();
}
}
private List<Map<String, Object>> getData() {
// 测试数据
List<Map<String, Object>> rsult = new ArrayList<>();
for (int i = 0; i < 5; i++) {
Map<String, Object> dataMap = new HashMap<>();
dataMap.put("title", "标题");
dataMap.put("subTitle", "副标题");
dataMap.put("name", "name" + i);
dataMap.put("sex", "sex");
dataMap.put("cardNo", "cardNo");
dataMap.put("address", "address");
dataMap.put("tel", "tel");
dataMap.put("level", "level");
dataMap.put("school", "school");
dataMap.put("buildNo", "buildNo");
dataMap.put("guardian", "guardian");
dataMap.put("relation", "relation");
dataMap.put("guardianTel", "guardianTel");
dataMap.put("program", "program");
dataMap.put("content", "content");
dataMap.put("date", "2018年4月5日");
rsult.add(dataMap);
}
return rsult;
}
}
freemarker的模板写法有很多种,可以参考gtlishujie的Freemarker提供了3种加载模板目录的方法
注意:这次实现方式导出来的word在移动端直接打开,会显示XML格式的内容,目前没有代码层面的解决方案。手动解决方案就是在电脑上打开,然后另存为doc或者docx格式的文档,在移动端就可以使用。
上一篇: 使用FreeMarker模板导出word
下一篇: 2019蓝桥杯B组第10题:灵能传输