乐于分享
好东西不私藏

Java 生成 Word+PDF:poi‑tl 模板填充 + Aspose‑words 转 PDF 真实项目复盘

Java 生成 Word+PDF:poi‑tl 模板填充 + Aspose‑words 转 PDF 真实项目复盘

业务开发几乎绕不开文档导出需求:合同、报表、报告单、审批单据,需要后端动态生成 Word 或者 PDF 返回给前端下载。

做这个需求的时候才发现,Java 生态里生成 Word/PDF 的工具非常多:POI、Freemarker + 模板、iText、PdfBox、Aspose…… 每一种网上都有大量示例。 但各个方案各有坑:有的排版难调、有的中文乱码、有的大文件内存爆炸、有的商业授权收费、有的 PDF 样式和 Word 对不齐。 我们项目目前采用一套很常见的组合(版本老旧,大家可以考虑新版本):

  • poi‑tl 1.12.2
    :基于 Apache POI,可视化 Word 模板填充数据,生成 docx 文档
  • aspose‑words 15.8.0
    :接收 poi‑tl 输出的 docx 文件,直接转换输出 PDF

这是一个兼具开发效率输出质量的黄金组合,能很好地覆盖从简单到复杂的各类文档需求。

网上很多 Demo 直接拿来就用,跑 Demo 的时候体验非常丝滑。但是上线生产之后,会遇到版本限制、授权、中文、大文件、复杂排版一系列现实问题。

今天基于我们正在使用的这两个具体版本,讲讲这套组合怎么落地、优势、致命短板,以及生产环境必须注意的风险点。


一、简单说明两个组件职责分工

  1. poi‑tl 1.12.2(com.deepoove)

基于 Apache POI 封装的模板引擎。不需要手写 POI 复杂的样式代码,直接用 Word 编辑 docx 模板,使用{{变量}}占位符,Java 传入数据,直接渲染生成 docx 文档。 擅长:表格循环渲染、图片渲染、列表、简单合并单元格。

  1. aspose‑words 15.8.0(com.aspose)

商业库,读取 docx 文档,高质量输出 PDF。解决 POI 生态原生缺少靠谱 PDF 转换的痛点。

整套流程:Word模板docx → poi‑tl填充业务数据 → 生成输出docx文件 → Aspose.Words读取docx → 导出PDF


二、最简核心代码示例(可直接参考)

   public void downloadPdf(ShareSubTableReqVO request, HttpServletResponse response) {        try {            String fileName = "生成报告.pdf";            TestEntity base = getBaseInfo(request);            Map<String, String> headPartData = getHeadPartData(base);            byte[] pdfBytes;            try (NiceXWPFDocument wordDocument = putNiceXWPFDocument(headPartData);                 ByteArrayOutputStream wordStream = new ByteArrayOutputStream()) {                wordDocument.write(wordStream);                try (InputStream inputStream = new ByteArrayInputStream(wordStream.toByteArray());                     ByteArrayOutputStream pdfStream = new ByteArrayOutputStream()) {                    Document pdfDocument = new Document(inputStream);                    pdfDocument.save(pdfStream, SaveFormat.PDF);                    pdfBytes = pdfStream.toByteArray();                }            }            String encodedFileName = URLEncoder.encode(fileName, StandardCharsets.UTF_8).replace("+""%20");            response.reset();            response.setCharacterEncoding(StandardCharsets.UTF_8.name());            response.setContentType(MediaType.APPLICATION_PDF_VALUE);            response.setHeader("Content-Disposition""attachment; filename=\"" + encodedFileName + "\"; filename*=UTF-8''" + encodedFileName);            response.setHeader("Cache-Control""no-store, no-cache, must-revalidate");            response.setHeader("Pragma""no-cache");            try (BufferedOutputStream outputStream = new BufferedOutputStream(response.getOutputStream())) {                outputStream.write(pdfBytes);                outputStream.flush();            }        } catch (Exception e) {            handleDownloadException(response, "生成 PDF 失败", e);        }    }    private NiceXWPFDocument putNiceXWPFDocument(Map<String, String> headPartData) {        File templateFile = new File("template/publicHeader.docx");        XWPFTemplate publicHeader = XWPFTemplate.compile(templateFile).render(headPartData);        NiceXWPFDocument main = publicHeader.getXWPFDocument();        //main里面填充其他数据        // 删除最后的分页符号        int summary = main.getBodyElements().size();        try {            // 注意下面有可能会误删段落            if (main.getBodyElements().get(summary - 1).getElementType().equals(BodyElementType.PARAGRAPH)) {                main.removeBodyElement(summary - 1);            }        } catch (Exception e) {            e.printStackTrace();        }        return main;    }

注意:aspose 默认会有水印,没有 license 授权的情况下,输出 Word/PDF 会生成评估水印。


三、poi‑tl 1.12.2 + aspose‑words 15.8.0组合优势

✅1. 开发效率高,模板可视化编辑,poi‑tl 直接用 Office 编辑 docx 模板,写{{xxx}}占位符;运营改格式、改文案,大部分场景不需要修改 Java 代码。对比原生 POI 手写代码操作单元格、样式,开发成本大幅降低。

✅2. Word→PDF 转换效果优秀, poi‑tl 本身没有 PDF 能力。Aspose‑words 的 docx 转 PDF 渲染质量远高于 LibreOffice、iText 等方案,表格、图片、页眉页脚、字体、复杂合并单元格,还原度很高,很少出现排版错乱。

✅3. poi‑tl 开源免费 Apache2 协议,无版权风险 poi‑tl 完全开源,可以放心商用,底层封装 POI,生态成熟。支持循环表格、图片插入、列表渲染,满足绝大多数业务单据需求。

✅4. 职责解耦 poi‑tl 只负责填充模板产出 docx;转换 PDF 交给 Aspose。两者各司其职,逻辑清晰。


四、这套组合的短板 & 生产大坑

🔴1. Aspose.Words 15.8.0 商业授权风险【最高优先级风险】

aspose‑words商业闭源组件

  • Maven 直接引入只是可以编译运行,不等于获得商用授权
  • 没有导入 license 证书,导出的 Word/PDF 会出现「Evaluation Only. Created with Aspose.Words」水印。
  • 企业商用,必须购买官方 license 证书;直接无授权用于线上业务,存在法务版权风险

🟡2. aspose‑words 15.8.0 版本老旧,存在已知 Bug 与安全漏洞

15.8.0 发布时间很早。

  • 对高版本 JDK(JDK17/JDK21)兼容性差,部分环境会出现反射报错、类加载异常。
  • 存在已知安全漏洞,不会再收到官方补丁。
  • 新版本 docx 部分高级特性,老版本 aspose 无法完美解析。

🟡3. poi‑tl 1.12.2 本身版本局限

1.12.2 为较老版本:

  • 依赖 POI 旧版本,处理超大 docx 文档,容易出现内存 OOM;
  • 部分复杂嵌套表格、复杂单元格合并场景会渲染异常;
  • Word 模板如果用户手动改动,把{{name}}占位符被 Word 自动拆分多段 XML,会出现变量无法识别,这是 poi‑tl 经典坑。

🟡4. 字体问题

服务器缺少中文字体,Aspose 转 PDF 中文会变成方框□。服务器必须部署宋体、微软雅黑等中文字体。

🟡5. 大文件导出内存压力

poi‑tl 底层 POI、Aspose 都把文档加载进内存处理。 如果大数据量导出大报表,直接内存暴涨,容易 OOM。不适合十万行级超大表格。

🟡6. 两个组件版本互相不绑定

poi‑tl 升级、aspose 版本升级,有可能出现 docx 格式兼容问题,需要回归测试。


五、项目落地必须做的几件事(生产 checklist)

  1. 授权确认
    :确认是否拥有 aspose‑words 正式商用 license,禁止破解 license 上线。
  2. 服务器字体部署
    :Linux 服务器安装中文字体,避免 PDF 中文方框乱码。
  3. 模板制作规范
    :编辑 docx 模板之后,不要手动修改 xml;占位符不要被 Word 自动拆分;复杂表格提前测试渲染效果。
  4. 大文件处理
    :大数据量文档,改为异步导出,存入对象存储,返回下载链接,避免同步接口 OOM、超时。
  5. JDK 升级注意:15.8.0 对 JDK17/JDK21 兼容性差,如果你们项目已经升级高版本 JDK,建议评估升级 aspose 新版本。
  6. 做异常捕获:文档转换失败要有重试、日志记录,不要直接抛出异常给前端。


poi‑tl + aspose‑words是业务文档导出体验很强的组合。 poi‑tl 开源免费,模板开发高效;Aspose 转换 PDF 的还原度在同类组件里属于顶尖。最大的风险不在技术,而在于 Aspose 商业版权,以及当前 15.8.0 版本老旧带来兼容性、安全问题。PDF 转换也可以改用 LibreOffice 开源方案。

去水印及中文乱码问题,大家也可以私信我。

我是 10 年 Java 后端程序媛,记录 JDK 升级、第三方对接、业务实战踩坑复盘等。