word模板生成实现方案
# word模板生成实现方案
传统word模板生成引擎主要采用FrameMark或Jasper构建模板文件并渲染生成;
# 存在问题
- 模板文件可读性差;
- 模板文件不可维护,开发人员模板修改及验证时间长;
优化方案:
替换采用poi-tl
Word模板引擎,使用docx文件作为模板附件进行渲染及导出;降低模板文件维护难度,提高模板可读性;
poi-tl
基于Apache POI的Word模板引擎,项目免费开源,可商用;原生支持文本、图片、表格、列表、批注、EL表达式等;
poi-tl
官网地址:https://deepoove.com/poi-tl/ (opens new window)
# 使用实例
核心依赖
poi低版本可使用低版本poi-tl,具体版本依赖关系可参考官网说明
maven依赖
<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>5.2.3</version> </dependency> <dependency> <groupId>com.deepoove</groupId> <artifactId>poi-tl</artifactId> <version>1.12.2</version> </dependency>
1
2
3
4
5
6
7
8
9
10
11
12
13创建及编辑模板template.docx
这是个word模板 参数name: {{name}}
1模板渲染及生成
String templatePath = "C:\\Users\\Hidewnd\\Desktop\\template.docx"; Map<String, Object> data = new HashMap<>(); data.put("name", "Sayi"); data.put("start_time", "2019-08-04"); XWPFTemplate template = XWPFTemplate.compile(templatePath).render(data); String targetPath = "C:\\Users\\Hidewnd\\Desktop\\outputDocx.docx"; template.writeAndClose(new FileOutputStream(targetPath));
1
2
3
4
5
6
7
图片、表格、列表循环等可通过配置类Configure
使用插件进行声明以实现不同效果
# 工具类封装
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.StrUtil;
import com.deepoove.poi.XWPFTemplate;
import com.deepoove.poi.config.Configure;
import com.deepoove.poi.config.ConfigureBuilder;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Map;
/**
* word模板生成工具类
* <p>
* 基于Poi-tl封装 官方文档:<a href="https://deepoove.com/poi-tl/">https://deepoove.com/poi-tl/</a>
* </p>
*
* @author hidewnd
*/
public class WordExportUtils {
/**
* 根据模板文件导出文件
*
* @param templateFile 模板附件
* @param targetName 目标文件名称
* @param data 数据Map,包含模板中需要替换的数据
* @return 生成文件对象
* @throws Exception 可能抛出的异常
*/
public static File exportByFile(File templateFile, String targetName, Map<String, Object> data) throws Exception {
ConfigureBuilder builder = Configure.builder();
return exportByFile(templateFile, targetName, data, builder.build());
}
/**
* 根据模板文件导出文件
*
* @param templateFile 模板附件
* @param targetName 目标文件名称
* @param data 数据Map,包含模板中需要替换的数据
* @param configure 配置参数项
* @return 生成文件对象
* @throws Exception 可能抛出的异常
*/
public static File exportByFile(File templateFile, String targetName, Map<String, Object> data, Configure configure) throws Exception {
checkTemplateFile(templateFile);
File targetFile;
try (XWPFTemplate template = XWPFTemplate.compile(templateFile, configure)) {
template.render(data);
String targetPath = formatTargetPath(templateFile.getParentFile().getAbsolutePath(), targetName);
template.writeToFile(targetPath);
targetFile = new File(targetPath);
}
return targetFile;
}
private static void checkTemplateFile(File templateFile) throws Exception {
if (templateFile == null || !templateFile.exists() || !templateFile.isFile()) {
throw new FileNotFoundException("template File not found.");
}
if (!FileUtil.getSuffix(templateFile).equals("docx")) {
throw new RuntimeException("template file type must be docx.");
}
}
private static String formatTargetPath(String targetParentPath, String targetName) {
String fileName = StrUtil.emptyToDefault(targetName, "targetFile.docx");
if (StrUtil.endWith(fileName, ".docx")) {
fileName = StrUtil.replaceLast(fileName, ".doc", ".docx");
}
if (!fileName.contains(".docx")) {
fileName += ".docx";
}
return targetParentPath + File.separator + fileName;
}
public static void exportByStream(InputStream templateStream, OutputStream outputStream, Map<String, Object> data) throws Exception {
ConfigureBuilder builder = Configure.builder();
exportByStream(templateStream, outputStream, data, builder.build());
}
public static void exportByStream(InputStream templateStream, OutputStream outputStream,
Map<String, Object> data, Configure configure) throws Exception {
try (XWPFTemplate template = XWPFTemplate.compile(templateStream, configure)) {
template.render(data);
template.write(outputStream);
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
帮助我改善此页面 (opens new window)
上次更新: 2025/08/25, 15:55:52