Apache POI操作Docx文档时踩坑指南

1、背景

Apache POI是什么

百度百科介绍:

Apache POI 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程序对Microsoft Office格式档案读和写的功能。

系统环境

前端:Vue2.7

后端:SpringBoot 2.3.12.RELEASE

文档编辑组件:Apache POI 4.1.2

开发工具:IDEA 2024.1

问题描述

文档中的变量以${里面是变量内容}的形式存在,在扫描文档变量时,会将里面的变量名展示在系统界面中。对变量名赋值后,会将变量替换。

如图1所示为模板文档,其中包含了一个变量,在系统界面中展示时以图2所示为例,但将内容替换到模板中变量后,内容却发生了变化;正常应该是四行"测试",但变量位置只有一行,剩下三行跑到了文档表格单元格的最下方。

2、Debug定位问题

先构思一个可能存在问题的节点列表:

① 前后端传参有误

② 替换内容时寻找替换位置有误

③ 替换内容时有误

下面开始一一排查。

排查是否前后端传参有误

先判断前后端传参是否有误,服务端以Debug启动,并在下载文档接口处进行断点,查看接口断点处接收到的构建参数。

下载文档接口断点处传入参数现实是正确的,四行测试内容。所以排除第一个可能。

排查替换内容时寻找替换位置有误

直接在替换完成后的地方打断点,查看替换完成后的表格单元格内容。

替换完成后的单元格内容只呈现一行测试,剩下三行同样是消失不见了,但位置是没有问题的,因为在替换之前这个位置是变量占位符的位置。

排查替换内容时有误

排查这项就麻烦了,需要进入替换方法一行一行的跟着看,但进入这个方法后,我看了一遍代码基本上初步确定问题了,但还是走一遍Debug确认一下。

通过断点查看XWPFTableCell cell的变化,在进入循环之前都是正常的,第一行测试正常替换掉了原占位变量。

跟着过了第一次循环,也就是将第二行测试放到第一行测试之后,也需要在第一行测试之后创建段落,但段落被创建到了单元格的最后了,也就导致了剩下所有的内容都被放到了最后。

3、解决问题

首先需要理解Apache POI在编辑Docx的基本概念,才能知道这里应该怎么改。

在Apache POI中依赖关系是:文档 -> 表格 -> 单元格 -> 段落 -> 文本片段。

而我们代码中:

java 复制代码
// 创建新段落
XWPFParagraph newParagraph = cell.addParagraph();

实际上是给单元格添加一个段落,而默认的段落位置就是在单元格的最后,这就是问题所在。

所以我们只需要在创建新段落的时候告诉POI要在哪里创建新段落即可,下面是将该逻辑单独抽象出来的方法:

java 复制代码
/**
 * 续写后续的行,除了第一行
 * @param cell 表格单元格
 * @param paragraph 命中关键词的行
 * @param lines 要续写的新文本
 */
private void continuedWritingLineToCell(XWPFTableCell cell, XWPFParagraph paragraph, String[] lines) {
    for (int i = 1; i < lines.length; i++) {
        // 使用目标段落的光标,并移动到段落的末尾
        XWPFParagraph newParagraph;
        int paragraphIndex = cell.getParagraphs().indexOf(paragraph);
        if (cell.getParagraphs().size() - 1 == paragraphIndex) { // 最后一个段落
            newParagraph = cell.addParagraph();
        }else{ // 中间的段落
            newParagraph = cell.insertNewParagraph(cell.getParagraphs().get(paragraphIndex + 1).getCTP().newCursor());
        }
        // 创建运行
        XWPFRun newRun = newParagraph.createRun();
        newRun.setText(lines[i]);
        this.copyFormatting(newParagraph, paragraph);

        // 更新 paragraph 引用,以便后续的段落插入正确的位置
        paragraph = newParagraph;
    }
}

4、总结

利用开源工具做一个业务的时候必须理解开源工具的基本构造,知道每一个方法是干啥的,调用方法之后会产生什么效果,否则只会越改越乱。

5、还有话说

而且,我解决这个问题的时候还发现了一个新问题,在这个方法内的前几行代码:

java 复制代码
for (int i = paragraph.getRuns().size() - 1; i >= 0; i--) {
    paragraph.removeRun(i);
}

这个操作是删除一个段落中的所有文本片段,但这样会导致变量如何不是自己独占一个段落时其他内容也会被删除。这个问题修改起来就扯的更多了,下次再展开说这个问题。

相关推荐
牛奶咖啡134 小时前
zabbix实现监控Apache、Nginx、php-fpm应用的实操保姆级流程
nginx·apache·zabbix·php-fpm·zabbix监控apache·zabbix监控nginx·zabbix监控php-fpm
de之梦-御风14 小时前
【工具分享】另一个免费开源的远程桌面服务-Apache Guacamole
开源·apache
tritone2 天前
在优豆云免费云服务器上搭建与配置Apache的实践笔记
服务器·网络·apache·1024程序员节
黑猫酋长2 天前
Apache Zeppelin:一款强大的数据分析和可视化工具
数据挖掘·数据分析·apache
belldeep2 天前
网络安全:Apache Druid 安全漏洞
网络安全·apache·druid
SelectDB2 天前
Apache Doris 4.0 版本正式发布:全面升级 AI 与搜索能力,强化离线计算
数据库·数据分析·apache
SelectDB2 天前
从 Snowflake 到 Apache Doris:Planet 实时分析成本直降 80%、查询加速 90 倍
数据库·数据分析·apache
一路向北⁢3 天前
基于 Apache POI 5.2.5 构建高效 Excel 工具类:从零到生产级实践
java·apache·excel·apache poi·easy-excel·fast-excel
Lion Long4 天前
PB级数据洪流下的抉择:从大数据架构师视角,深度解析时序数据库选型与性能优化(聚焦Apache IoTDB)
大数据·性能优化·apache·时序数据库·iotdb
ybb_ymm5 天前
从需求开始至架构设计的适用于商家及小吃摊的点餐小程序
小程序·apache