在 Spring Boot 中根据 Word 模板导出包含表格、图表等复杂格式的文档,可通过以下方案实现。以下是综合多篇技术博客和最佳实践的完整指南:
一、技术选型与方案对比
方案 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
poi-tl | 直接操作 Word 模板,支持动态循环、合并单元格,开发效率高 | 需熟悉模板语法,图表需结合其他库生成 | 快速开发,模板复杂度中等 |
Apache POI | 灵活性强,支持底层操作文档结构 | 代码量大,模板处理复杂度高 | 高度定制化需求 |
Freemarker/Thymeleaf | 模板语法简单,适合文本替换 | 需 XML 转换,图表生成困难 | 纯文本或简单表格 |
推荐方案 :优先使用 poi-tl(结合 JFreeChart 生成图表),兼顾开发效率和功能完整性。
二、poi-tl 实现步骤(含表格与图表)
1. 添加依赖
xml
<!-- poi-tl 核心依赖 -->
<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.12.2</version>
</dependency>
<!-- 图表生成依赖 -->
<dependency>
<groupId>org.jfree</groupId>
<artifactId>jfreechart</artifactId>
<version>1.5.4</version>
</dependency>
2. 设计 Word 模板
-
表格模板:
css{{userList}} 姓名 | 性别 | 年龄 ---|---|--- [name] | [sex] | [age]
-
图表处理:
使用 JFreeChart 生成图表图片,插入到 Word 指定位置(poi-tl 不支持直接生成图表)。
3. 代码实现
arduino
// 配置渲染策略(合并单元格、循环渲染)
Configure config = Configure.builder()
.useSpringEL()
.bind("list1", new LoopRowTableRenderPolicy()) // 行循环
.bind("tableList", new ReportDataPolicy()) // 自定义合并策略
.build();
// 加载模板并渲染数据
XWPFTemplate template = XWPFTemplate.compile("template.docx", config)
.render(dataMap);
// 输出到响应流(支持下载)
template.write(response.getOutputStream());
4. 自定义合并单元格策略
scala
public class ReportDataPolicy extends DynamicTableRenderPolicy {
@Override
public void render(XWPFTable table, Object data) {
// 解析数据并动态插入行
List<BsReportData> list = JSON.parseArray(JSON.toJSONString(data), BsReportData.class);
for (BsReportData item : list) {
RowRenderData row = Rows.of(item.getField1(), item.getField2()).create();
table.insertNewTableRow().render(row);
}
}
}
三、Apache POI 直接操作方案
1. 核心代码(表格生成)
scss
XWPFDocument document = new XWPFDocument();
XWPFTable table = document.createTable(3, 3); // 3行3列
table.getRow(0).getCell(0).setText("标题");
// 动态填充数据
for (int i=0; i<dataList.size(); i++) {
table.getRow(i+1).getCell(0).setText(dataList.get(i).getName());
}
2. 图表生成(需结合 JFreeChart)
ini
// 生成柱状图
JFreeChart chart = ChartFactory.createBarChart(...);
// 转换为 BufferedImage
BufferedImage image = chart.createBufferedImage(400, 300);
// 插入 Word
XWPFParagraph paragraph = document.createParagraph();
XWPFRun run = paragraph.createRun();
run.addPicture(new ByteArrayInputStream(toByteArray(image)), XWPFDocument.PICTURE_TYPE_PNG, "chart.png", Units.toEMU(400), Units.toEMU(300));
四、模板设计最佳实践
-
占位符规范
- 简单变量:
{{fieldName}}
- 循环列表:
{{listName}}
+ 表头占位符(如[name]
)
- 简单变量:
-
复杂格式处理
- 合并单元格:通过代码动态操作
XWPFTableRow
和XWPFTableCell
- 样式继承:在 Word 模板中预定义样式,避免代码中硬编码格式
- 合并单元格:通过代码动态操作
-
图表兼容性
- 优先使用 SVG 格式(高分辨率)
- 测试不同 Office 版本的显示效果
五、方案对比与选择建议
场景 | 推荐方案 | 理由 |
---|---|---|
快速原型开发 | poi-tl | 模板直观,开发周期短 |
高定制化需求 | Apache POI | 完全控制文档结构和样式 |
纯文本/简单表格 | Freemarker | 模板编写简单,无需处理二进制流 |
六、常见问题解决
-
图表显示异常
- 确保图片分辨率 ≥ 300dpi
- 使用
XWPFRun.addPicture()
时指定正确 MIME 类型
-
模板变量不生效
- 检查占位符语法是否与代码中的键名一致
- 使用
Configure.builder().useSpringEL()
启用 SpEL 表达式支持
-
大文件内存溢出
- 使用
SXSSFWorkbook
替代XSSFWorkbook
- 分页写入数据,避免一次性加载全部内容
- 使用
通过上述方案,可灵活实现包含动态表格、图表等复杂格式的 Word 导出功能。推荐优先采用 poi-tl 方案平衡开发效率与功能完整性,复杂场景可结合 Apache POI 进行深度定制。