cover_image

Word文档导出的实践方案

崔荣奎 新东方技术
2021年05月06日 02:10

背景简介

基于业务需求,我们需要将试题组装成试卷,然后导出 .docx 格式的 word 文档,同时需要支持公式数据在 word 中正常展示,并且能够重新编辑。


方案选择

导出 word 文档有多种方式,根据业务需求和开发成本的考量,
主要考虑了基于 Freemarker 和 Poi-tl 的两种方式,实践之后最终确定使用 Poi-tl 的方式实现。其优点主要有以下两个方面:
  • 模板配置方便,可以直接使用 .docx 文档做模板,只需要根据相应的语法创建模板文件即可。
  • 本身就是java语言,使用java代码操作更方便。
  • API相对比较全面,可以满足目前需求中的绝大多数情况。
放弃使用 Freemarker 方式的主要原因:Freemarker 中添加图片需要确定图片的位置,并在模版中图片位置添加占位符,而试题中的图片位置在填充试题内容后才能确定,因此使用 Freemarker 的方式处理试题中的图片比较繁琐。

以下为调研 word 导出方案之后的各个方案对比

方案
移植性
功能性
易用性

Poi-tl

Java跨平台

Word模板引擎

基于Apache POI

Apache POI

Java跨平台

Apache项目,功能丰富

文档不全。附:Apache POI Word快速入门

Freemarker

XML跨平台

仅支持文本,很大的局限性

不推荐,需要维护XML结构,代码后期不可维护

OpenOffice

部署OpenOffice,移植性较差

-

需要了解OpenOffice的API

HTML浏览器导出

依赖浏览器的实现,移植性较差

HTML不能很好的兼容Word的格式

-

Jacob、winlib

Windows平台

-

复杂,完全不推荐使用

具体实现

Poi-tl 简介

Poi-tl 是Word模板引擎,能够基于Word模板和数据生成新的文档。

Poi-tl API

所有标签默认以 {{ 作为开头,以 }} 作为结尾。以下介绍不同格式的数据各自的填充方式。

文本标签

{{name}}
{{author}}
{{link}}
{{anchor}}

数据格式

Map<String, Object> data = new HashMap<>();
data.put("name", "Sayi");
data.put("author", new TextRenderData("000000", "Sayi"));
data.put("link", new HyperlinkTextRenderData("website", "http://deepoove.com"));
data.put("anchor", new HyperlinkTextRenderData("anchortxt", "anchor:appendix1"));

图片标签以@开始 (注意:图片实体必须设置size)

{{@localImg}}
{{@streamImg}}
{{@urlImg}}
{{@bufferImg}}

数据格式

Map<String, Object> data = new HashMap<>();
// 本地图片
data.put("localImg", Pictures.ofLocal("sayi.png").size(120, 120).create());

// 图片流
data.put("streamImg", Pictures.ofStream(new FileInputStream("logo.jpeg"), PictureType.JPEG)
.size(100, 120).create());

// 网络图片(注意网络耗时对系统可能的性能影响)
data.put("urlImg", Pictures.ofUrl("http://deepoove.com/images/icecream.png", PictureType.PNG)
.size(100, 100).create());

// java 图片
data.put("bufferImg", Pictures.ofBufferedImage(bufferImage, PictureType.PNG)
.size(100, 100).create());

表格标签以#开始

{{#table}}

数据格式

// 一个2行2列的表格
Map<String, Object> data = new HashMap<>();
data.put("table", Tables.of(new String[][] {
new String[] { "00", "01" },
new String[] { "10", "11" }
}).border(BorderStyle.DEFAULT).create());

特殊格式–区块对:
区块对由前后两个标签组成,开始标签以?标识,结束标签以/标识

{{?person}}
Hi {{name}}!
{{?models}}
{{modelName}}
{{/models}}
{{/person}}

数据格式

Map<String, Object> name = new HashMap<>();
name.put("name", "Sayi");

List<Map<String, Object>> models = new ArrayList<>();
Map<String, Object> modelName = new HashMap<>();
modelName.put("modelName", "model");
models.add(modelName);
Map<String, Object> modelMap = new HashMap<>();
modelMap.put("models", models);

List<Map<String, Object>> person = new ArrayList<>();
person.add(name);
person.add(models);

Map<String, Object> data = new HashMap<>();
data.put("person", person);

使用区块对可以更加方便灵活的处理数据,当区块对中的值为 null 时,该数据则不会被填充;区块对支持遍历输出,同时支持区块对嵌套。

Getting Started

  1. 添加 maven 依赖

<dependency>
<groupId>com.deepoove</groupId>
<artifactId>poi-tl</artifactId>
<version>1.9.1</version>
</dependency>

  1. 新建Word文档template.docx,word 内容包含标签 {{title}}

  2. 代码实现 word 导出

XWPFTemplate template = XWPFTemplate.compile("template.docx").render(
new HashMap<String, Object>(){{
put("title", "Hi, poi-tl Word模板引擎");
}});
template.writeAndClose(new FileOutputStream("output.docx"));

结语

使用 Poi-tl 实现 word 文档的导出方便且快速,本文介绍了 Poi-tl 的简单使用,Poi-tl 的功能远不止这些,它还为我们提供了许多的可选插件,同时支持插件自实现,后续我们将继续共同学习探讨。PS:对于 word 文档中公式的展示,我们会在后续的文章中讨论。

参考:

poi-tl API:http://deepoove.com/poi-tl


图片


继续滑动看下一个
新东方技术
向上滑动看下一个