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);
}

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

相关推荐
源图客7 天前
Apache Flink 2.3.0(最新版本)
大数据·flink·apache
代立冬10 天前
927 个 PR:Apache SeaTunnel 3.0.0 正式发布!AI 时代,数据集成正在迎来新的可能
人工智能·apache
zhongben_keji11 天前
家政小程序开发全解:经营模式、品类矩阵、派单约束与信用核验
线性代数·矩阵·apache
weixin_1772972206912 天前
二手旧物回收小程序先要解决的不是下单,是“上门之后价格变了怎么办“
小程序·apache
努力变的更好12 天前
H5与小程序之间的回调
前端·小程序·apache
十碗饭吃不饱14 天前
Apache POI 踩坑:setFontFamily 二次设置字体不生效、样式残留问题深度解析
java·word·apache
心随_风动15 天前
Ubuntu系统安装Apache服务教程
linux·ubuntu·apache
ly768917 天前
Kafka 消费者重试与死信:Spring for Apache Kafka 的边界设计
spring·kafka·apache·并发消费·消费者重试·死信主题·位移提交
HoneyMoose17 天前
Ubuntu Apache 配置多站点提示 SSL 错误
ubuntu·apache·ssl
骑着蜗牛撵大象32717 天前
一套引擎撑起树表双模型:Apache IoTDB 2.0 与 TsFile 的选型答卷
apache·时序数据库·iotdb·tsfile·树模型·表模型