Java基于POI填充Word模版

最近有个需求,就是要根据用户订单信息来填充现有的Word模版,从而生成一个在线电子文档,再通过易企签来实现线上签字确认。

我们先来看看模版

这是我们项目上的大概文档,因为要涉及到一些文字替换和段落删减,引入了一些符号替换规则。

POI操作Word的常用属性

在操作之前,我们需要了解一下属性和方法

  • XWPFDocument:用来获取或者创建一个Word文档
  • XWPFParagraph:标题、文档、表格等
  • XWPFRun:同样风格的一段文本
  • XWPFTable:表格
  • XWPFTableRow:表格中的一行
  • XWPFTableCell:表格中的一个单元格

填充Word模版

现在我们就开始实践-填充我们上面的模版

引入POI包

1
2
3
4
5
6
7
8
9
10
11
12
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.apache.poi/poi-ooxml -->
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>

这里的变量值是<poi.version>4.1.0</poi.version>

获取文档对象

由于我的文档模版是存储在GridFS中(其实我更倾向于MinIO),所以我的通过文件流的形式读取

1
2
3
4
5
//通过文件读取
XWPFDocument document = new XWPFDocument(OPCPackage.open(filePath));

//通过文件流读取,in是InputStream对象
XWPFDocument document = new XWPFDocument(in);

获取到文档之后,就可以从中获取到对应的XWPFParagraph对象

1
List<XWPFParagraph> paragraphs = document.getParagraphs();

然后我们可以从XWPFParagraph获取到Text即段落中的内容,但是我们要替换段落中的内容的话必须在XWPFRun才行。所以需要循环遍历去处理

1
2
3
4
5
6
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {

}
}

这里我们可以通过run.toString()获取到内容,并通过run.setText进行替换。在我们获取到文本之后,要匹配出对象的关键字,所以这里需要一个正则匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
private static List<String> getKeywords(String source) {
String regStr = "\\$\\{[a-zA-Z0-9]+\\}";
List<String> matchStrs = new ArrayList<>();

Pattern patten = Pattern.compile(regStr);
Matcher matcher = patten.matcher(source);

while (matcher.find()) {
matchStrs.add(matcher.group());
}

return matchStrs;
}

我们通过getKeywords获取到模版中的关键字后,再从Map对象中去取出对应的值,然replace

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for (XWPFParagraph paragraph : paragraphs) {
List<XWPFRun> runs = paragraph.getRuns();
for (XWPFRun run : runs) {
String value=run.ToString();
List<String> keywords = getKeywords(value);
for (String keyword : keywords
) {
String key = keyword.replace("${", "").replace("}", "");
String keyValue = sourceTextMap.get(key);
if (StringUtils.isEmpty(keyValue)) {
keyValue = "";
}
value = value.replace(keyword, keyValue);
}
run.setText(value,0);

}
}

一般的内容可以通过${key}的方式来替换,那么遇到表格怎么办呢?我继续来处理表格

1
List<XWPFTable> tables = document.getTables();

我这里先只针对我的模版进行处理,如果是遇到多个XWPFTable那么久根据情况自行处理吧

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
for (int i = 1; i < tableList.size(); i++) {
XWPFTableRow newRow = table.createRow();
List<XWPFTableCell> cells = newRow.getTableCells();
for (int j = 0; j < cells.size(); j++) {
XWPFTableCell cell = cells.get(j);
cell.setText(tableList.get(i - 1)[j]);
}
}
if (!ObjectUtils.isEmpty(tableBottom)) {
XWPFTableRow tableRow = table.createRow();
List<XWPFTableCell> footerCells = tableRow.getTableCells();
for (int j = 0; j < footerCells.size(); j++) {
XWPFTableCell cell = footerCells.get(j);
if (!StringUtils.isEmpty(tableBottom[j])) {
XWPFParagraph paragraph = cell.addParagraph();
XWPFRun xwpfRun = paragraph.createRun();
xwpfRun.setText(tableBottom[j]);
xwpfRun.setFontSize(12);
xwpfRun.setFontFamily("黑体");
xwpfRun.setBold(true);
xwpfRun.setColor("000000");
} else {
cell.setText(tableBottom[j]);
}

}

}

这里就会根据我们的传入的tableList即表格数据进行填充。根据tableBottom数组来添加一些样式。最后来看看我们做出的效果,首先是用Swagger调用接口

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
{
"fileName":"xxx订单导出.doc",
"filePath":"order_template.docx",
"headerContent":{
"orderDate":"2020-03-26 10:25",
"planDate":"202-03-27",
"orderNo":"20200325000074645",
"agreeNo":"80192000035",
"orderUnitNo":"155283",
"orderUnitName":"小卖部农业开发有限公司",
"name":"IT界摸鱼专家",
"phone":"138888888",
"bankNo":"6666666666666666573",
"owe":"小鸡炖蘑菇有限公司开发区分公司",
"owePrice":"16775.90",
"zh":"壹万伍仟捌佰贰拾伍圆零角零分",
"isDiaplay":"",
"memo":"小鸡炖蘑菇专用配送"
},
"tableList":[
[
"113029022",
"小鸡炖蘑菇了不起",
"2",
"T",
"50",
"2000.00",
"300000.00",
""
],
[
"113029044",
"小鸡炖蘑菇真棒",
"1.5",
"T",
"75",
"3000.00",
"50000.00",
""
],
[
"113029077",
"小鸡炖蘑菇好耶",
"1.275",
"T",
"85",
"2020.50",
"900000.50",
"赠品"
],
[
"113040057",
"小鸡炖蘑菇",
"10.025",
"T",
"255",
"12030.00",
"200000.20",
""
]
],
"tableBottom":[
"合计",
"壹万伍仟捌佰贰拾伍圆整",
"55.275",
"",
"852",
"",
"2255114.00",
"60.32T"
]
}

上面是我传参数,最后导出的Word文档是这样的

当然,这里还有一些细节需要处理。

#
You forgot to set the qrcode for Alipay. Please set it in _config.yml.
You forgot to set the qrcode for Wechat. Please set it in _config.yml.
You forgot to set the business and currency_code for Paypal. Please set it in _config.yml.
You forgot to set the url Patreon. Please set it in _config.yml.
Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×