- 引入Apache POI依赖
- 制作word模板
- 加载word模板,读取word模板的内容,替换占位符内容
- 将修改后文档另存为
一、基础示例
1. 引入依赖
在POM文件中引入相关依赖
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
2. 制作模板
新建一个word文档,需要动态替换的位置使用占位符,后面匹配到该占位符将数据填充到该位置即可。对于动态表格(项次条数不确定),在表格的第一行使用占位符 ${dynamicTable} 标记,表明该表格是动态表格,后面循环创建行并插入数据。

3. 读取模板
-
通过 XWPFDocument 加载模板文件
// 读取文件
InputStream is = new FileInputStream("这里填模板位置/inFile.docx");
XWPFDocument doc = new XWPFDocument(is); -
替换文本内容
遍历段落替换占位符
for (XWPFParagraph paragraph : doc.getParagraphs()) {
String text = paragraph.getText();
if (text != null && text.contains("${name}")) {
text = text.replace("${name}", "张三");
paragraph.getRuns().get(0).setText(text, 0);
}
}
- 处理表格
定位表格并填充数据:
XWPFTable table = doc.getTables().get(0);
// 示例:替换表格第一行第一列的内容
table.getRow(0).getCell(0).setText("数据1");
4. 导出文件
将修改后的文档写入新文件:
FileOutputStream out = new FileOutputStream("输入文件的位置/outFile.docx");
doc.write(out);
out.close();
doc.close();
二、案例
同时导出四个文件,有些文件既有普通固定表格,又有动态表格,又有文本。
从数据库查询数据,将每个字段数据通过key-value的形式存储在map集合中。动态数据存储在一个map中,静态数据存储在一个map中。


导出方法处理动态表格、段落、静态表格
// 模板位置
String[] templatePathArr = {"d:/excel/模板1.docx",
"d:/excel/模板2.docx",
"d:/excel/模板3.docx",
"d:/excel/模板4.docx"};
// 输出位置
String[] outFilePathArr = {"d:/tmp/" + projectName +"-文档1.docx",
"d:/tmp/" + projectName + "-文档2.报告.docx",
"d:/tmp/" + projectName + "-文档3.docx",
"d:/tmp/" + projectName + "-文档4.docx"};
for (int i = 0; i < templatePathArr.length; i++) {
// 读取Word模板
XWPFDocument doc = new XWPFDocument(new FileInputStream(templatePathArr[i]));
// 处理动态表格(移除标识行)
for (XWPFTable table : doc.getTables()) {
if (table.getRows().size() > 0) {
String firstCellText = table.getRow(0).getCell(0).getText();
if (firstCellText != null && firstCellText.contains("${dynamicTable}")) {
// 处理这个动态表格
processDynamicTable(table, dynamicData);
}
}
}
// 替换所有固定占位符
for (XWPFParagraph p : doc.getParagraphs()) {
replaceText(p, data);
}
// 处理静态表格
for (XWPFTable table : doc.getTables()) {
for (XWPFTableRow row : table.getRows()) {
for (XWPFTableCell cell : row.getTableCells()) {
for (XWPFParagraph p : cell.getParagraphs()) {
replaceText(p, data);
}
}
}
}
// 4. 保存到新文件
FileOutputStream out = new FileOutputStream(new File(outFilePathArr[i]));
doc.write(out);
doc.close();
out.close();
}
处理文本和固定表格方法
// 处理段落和固定表格并替换占位符
private static void replaceText(XWPFParagraph p, Map<String, String> data) {
// 获取整个段落的文本
String text = p.getText();
if (text != null && text.contains("${")) {
// 替换所有占位符
for (String key : data.keySet()) {
text = text.replace("${" + key + "}", data.get(key));
}
// 关键:清空原有内容再设置新内容
for (XWPFRun run : p.getRuns()) {
run.setText("", 0);
}
if (!p.getRuns().isEmpty()) {
p.getRuns().get(0).setText(text);
}
}
}
处理动态表格方法
// 填充动态表格
private void processDynamicTable(XWPFTable table, Map<String, Object> dynamicData) {
// 1. 移除标识行(第一行:${dynamicTable})
table.removeRow(0);
// 2. 移除占位符示例行
if (table.getRows().size() > 1) {
table.removeRow(1);
}
// 3. 处理表格
List<Object[]> tableData = new ArrayList<>();
String header = table.getRow(0).getCell(1).getText().trim(); // 获取表格第1行的第2列标题
int cellNum = table.getRow(0).getTableCells().size(); // 获取表格有多少列
if (header.contains("投资项目应用")) {
// 项目资金预算表
List<PatentEconomicItem> economicList = (List<PatentEconomicItem>) dynamicData.get("economicList");
// 转成数组
for (PatentEconomicItem patentEconomicItem : economicList) {
Object[] row = {
patentEconomicItem.getProjectApplicantion(),
patentEconomicItem.getCost(),
patentEconomicItem.getOrigin()
};
tableData.add(row);
}
} else if (header.contains("姓名")) {
// 主要人员参与表
List<PatentPeopleItem> peopleList = (List<PatentPeopleItem>) dynamicData.get("peopleList");
// 转成数组
for (PatentPeopleItem patentPeopleItem : peopleList) {
Object[] row = {
patentPeopleItem.getName(),
patentPeopleItem.getQualification(),
patentPeopleItem.getRank(),
patentPeopleItem.getProjectTask()
};
tableData.add(row);
}
} else if (header.contains("项次")) {
//自检表
List<PatentCheckList> checkList = (List<PatentCheckList>) dynamicData.get("checkList");
// 转成数组
for (PatentCheckList item : checkList) {
Object[] row = {
item.getItemNum(),
item.getResult(),
item.getRemark()
};
tableData.add(row);
}
}
// 填充动态表格数据
for (int i = 0; i < tableData.size(); i++) {
XWPFTableRow newRow = table.createRow();
// 第一列是序号,自动生成
newRow.getCell(0).setText(String.valueOf(i + 1));
// 填充其他列
for (int j = 1; j < cellNum; j++) {
newRow.getCell(j).setText(tableData.get(i)[j - 1].toString());
}
}
}
前端创建a标签下载
