时效提示:本文写于 2020 年,部分 API、配置和工具版本可能已经变化,参考时请以官方最新文档为准。

需求场景

这个需求对于ERP或者银行项目还是比较常见的,用户需要下载word文件or在线预览 -> 打印合同,表格 文件等。

本次需求有 1.文档中的字符串替换 2.表格生成 3. 图片插入替换 4. 批量插入图片

技术方案

  1. 模板 + Word XML 文档(*.xml) -> 生成xml文件后修改后缀为 .doc

  2. docx4j 直接操作 *.docx:把 .docx(本质是 zip 包里的 OOXML)解析成 Java 对象树来读写,能精细控制样式、表格、页眉页脚,也支持导出 PDF。功能最全,但 API 比较底层,简单的占位符替换用它偏重。资料:docx4j 官网、GitHub plutext/docx4j。

  3. XWPFTemplate(poi-tl):基于 Apache POI 的 Word 模板引擎,核心类就是 XWPFTemplate。在 .docx 模板里写 {{name}} 这样的标签,代码里传一个 Map 即可渲染,内置文本、图片、表格、列表、循环等能力,中文文档完善,“模板 + 数据”场景比方案一更省事,也直接生成 .docx。资料:poi-tl 中文文档、GitHub Sayi/poi-tl。

实现讲解

技术方案一:

模板 + Word XML 文档(*.xml) -> 生成xml文件后修改后缀为 .doc

技术准备

其中模板语言选用的是freemarker. 也可以使用jsp,velocity,thymeleaf等。 本文选用freemarker的原因在于文档较多,技术也比较老,大家都应该会一点点。

freemarker 语法简介: Freemarker 语法详解

如果需要用到输出转义 -> ${object?html}

步骤一

  1. 编辑word文档,将其中需要动态替换的字符串-> ${xxx}

  2. 需要动态替换的图片处理好(一个位置一个不同的图片哦,如果相同,word只会存储一份)

  3. 表格处理,需要for处理的表格,处理为两行,方便后期用模板语言处理

步骤二

将word文件另存为:

word

步骤三

编辑xml文件,查询${xxx}是否完整,没有错位

  1. 动态替换的图片的内存存储在 binarydata中,进行替换
    public static String getImageStr(InputStream in) {
        byte[] data = null;
        try {
            data = new byte[in.available()];
            in.read(data);
            in.close();
            BASE64Encoder encoder = new BASE64Encoder();
            return encoder.encode(data);
        } catch (IOException e) {
            log.error("获取图片Str失败", e);
        }
        return "";
    }

其中批量插入图片 -> 使用freemarker导出word并动态插入多张图片

  1. 表格需要找到相关对称的地方,用freemarker <#list>语法处理即可

  2. 文件处理完毕 输出为后缀为.doc文件即可

项目中个人Freemarker配置

        configuration.setDefaultEncoding("utf-8");
        //允许对象不存在
        configuration.setClassicCompatible(true);
       //不对数字进行格式化 
        configuration.setNumberFormat("0");

注意点

  1. 比如你用到了 word 华文仿宋GB2312字体,一定要看自己电脑是否装了这个字体,否则符号 数字 等会分割开,${xxx}在xml中会分隔开,达不到替换的目的
  1. 早期的WPS可能不支持 word.doc 升级下wps,而不是单纯的换技术

小结

  • 方案一(Freemarker + Word XML):零额外依赖,适合格式固定的老项目;产物是 .doc,Word 打开时依赖本机字体,见上文注意点。

  • 方案二(docx4j):需要程序化精细控制文档结构时选它。

  • 方案三(poi-tl):新项目做“模板填充”时的首选,直接生成 .docx。