JAVA 使用Apache POI合并Word文檔并保留批注的實現

一、需求背景

在實際工作中,我們經常需要將多個Word文檔合并成一個文件。但當文檔中包含批注(Comments)時,傳統的復制粘貼會導致批注丟失或引用錯亂。本文將介紹如何通過Java和Apache POI庫實現保留批注及引用關系的文檔合并功能。

二、技術選型

核心依賴

<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>5.3.0</version> <!-- 建議使用最新版本 -->
</dependency>
<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-full</artifactId><version>5.3.0</version>
</dependency>

三、實現原理詳解

核心思路

  1. 創建目標文檔作為合并容器
  2. 遍歷每個源文檔的段落
  3. 重建批注映射關系(避免ID沖突)
  4. 復制段落內容并更新批注引用
  5. 保存合并后的文檔

關鍵代碼解析

public static void mergeDocuments(List<String> sourcePaths, String outputPath) throws Exception {// 參數校驗if (sourcePaths == null || sourcePaths.isEmpty()) {throw new IllegalArgumentException("sourcePaths is empty");}// 1. 創建目標文檔var targetDoc = new XWPFDocument();// 創建批注容器(重要!)var targetComments = targetDoc.createComments();// 批注ID計數器(從0開始)BigInteger nextCommentId = BigInteger.ZERO;for (String sourceFile : sourcePaths) {try (var srcDoc = new XWPFDocument(new FileInputStream(sourceFile))) {// 2. 遍歷每個段落for (var sourcePara : srcDoc.getParagraphs()) {var newPara = targetDoc.createParagraph();// 3. 批注ID映射表(舊ID -> 新ID)Map<BigInteger, BigInteger> commentIdMap = new HashMap<>();// 4. 處理含批注引用的文本for (var sourceRun : sourcePara.getRuns()) {if (sourceRun.getCTR().sizeOfCommentReferenceArray() <= 0) {continue; // 跳過無批注的文本}// 處理每個批注引用for (var commentRef : sourceRun.getCTR().getCommentReferenceList()) {// 獲取源批注內容var sourceComment = srcDoc.getCommentByID(commentRef.getId().toString());// 在目標文檔創建新批注var targetComment = targetComments.createComment(nextCommentId);// 復制批注內容(關鍵步驟!)targetComment.getCtComment().set(sourceComment.getCtComment().copy());// 設置新IDtargetComment.getCtComment().setId(nextCommentId);// 保存ID映射關系commentIdMap.put(commentRef.getId(), nextCommentId);// ID自增(避免重復)nextCommentId = nextCommentId.add(BigInteger.ONE);}}// 5. 復制段落XML并更新批注IDString xml = sourcePara.getCTP().xmlText();// 替換所有批注ID引用for (var comment : commentIdMap.entrySet()){xml = xml.replaceAll("w:id=\"" + comment.getKey() + "\"", "w:id=\"" + comment.getValue() + "\"");}// 將修改后的XML載入新段落newPara.getCTP().set(CTP.Factory.parse(xml));}}}// 6. 保存合并結果try (FileOutputStream fos = new FileOutputStream(outputPath)) {targetDoc.write(fos);}targetDoc.close();
}

四、關鍵技術點

1. 批注ID重映射機制

  • 問題:不同文檔可能有重復的批注ID
  • 解決方案
    • 創建全局計數器 nextCommentId
    • 為每個批注生成新ID
    • 維護commentIdMap映射表

2. XML層級操作

  • 直接操作CTP對象:獲取段落底層XML結構
  • 正則替換:批量更新批注引用ID
xml = xml.replaceAll("w:id=\"" + oldId + "\"", "w:id=\"" + newId + "\"");

3. 內存管理

  • 使用try-with-resources確保資源釋放
try (var srcDoc = new XWPFDocument(new FileInputStream(sourceFile))) {// 處理文檔...
} // 自動關閉流

五、功能擴展建議

  1. 支持表格合并
for (XWPFTable table : srcDoc.getTables()) {// 復制表格到targetDoc
}
  1. 處理圖片/圖表
for (XWPFPictureData picture : srcDoc.getAllPictures()) {// 復制圖片數據
}
  1. 保留格式樣式
newPara.getCTP().setPPr(sourcePara.getCTP().getPPr());

六、注意事項

  1. 性能優化:處理大文檔時建議分塊處理
  2. ID沖突:必須重新映射批注ID
  3. 格式兼容性
    • 支持wps、office。
    • 支持docx格式
    • 不同Word版本可能有樣式差異
  4. 異常處理:實際生產需增加:
    catch (IOException | XmlException e) {// 處理解析異常
    }
    

七、總結

本文實現的合并方案具有以下優勢:

  • ? 完美保留批注及引用關系
  • ? 避免ID沖突的智能映射
  • ? 底層XML操作確保格式兼容
  • ? 靈活的擴展性

適用場景:法律文檔合并、論文修訂稿整合、團隊協作文檔匯總等需要保留批注的場景。

技術交流:歡迎在評論區留言討論!


附錄:核心依賴說明

依賴包作用
poi-ooxml提供XWPFDocument等基礎操作類
poi-ooxml-full支持完整的OOXML特性解析
xmlbeans底層XML操作依賴(自動傳遞)

建議在實際使用時注意:

  1. 使用POI版本(本文基于5.3.0)
  2. 處理10MB+文檔時增加JVM內存:
java -Xmx512m -jar yourApp.jar

此方案已通過以下環境驗證:

  • Java 11+
  • Apache POI 5.3.0
  • Microsoft Word 2016/365

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/91668.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/91668.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/91668.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Linux的服務管理工具:`systemd`(`systemctl`)和`SysVinit ` 筆記250718

Linux的服務管理工具:systemd(systemctl)和SysVinit 筆記250718 Linux的服務管理工具 Linux 的服務管理工具隨著發行版和初始化系統的發展而演變。以下是主要的服務管理工具及其對應的初始化系統&#xff1a; 1. systemd (現代主流標準) 初始化系統&#xff1a; 是絕大多數…

Couchbase 可觀測性最佳實踐

Couchbase 介紹 Couchbase 是一個開源的分布式 NoSQL 數據庫&#xff0c;專為高性能和高可擴展性設計&#xff0c;適用于實時數據處理的企業應用。它結合鍵值存儲和文檔數據庫的優勢&#xff0c;支持 JSON 文檔存儲&#xff0c;并通過 N1QL&#xff08;類 SQL 查詢語言&#x…

構建基于MCP的LLM聊天機器人客戶端開發指南

引言 在當今人工智能技術快速發展的時代&#xff0c;大型語言模型(LLM)已成為構建智能應用的核心組件。MCP(Modular Conversational Platform)作為一個強大的對話平臺&#xff0c;為開發者提供了將LLM能力與自定義工具集成的標準化方式。本文將詳細介紹如何使用Python開發一個…

接口測試的原則、用例與流程詳解

&#x1f345; 點擊文末小卡片&#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 一、接口的介紹軟件測試中&#xff0c;常說的接口有兩種&#xff1a;圖形用戶接口&#xff08;GUI&#xff0c;人與程序的接口&#xff09;、應用程序編程接口&am…

ubuntu 22.02 帶外進單用戶拯救系統

不停地按 F7 &#xff0c;然后進到 menu &#xff0c;選擇 ubuntu &#xff0c;然后按下 ESC &#xff0c;然后瞬間會刷一個 ubuntu 的選項&#xff08;默認是在第一的位置&#xff0c;直接快速按下 e&#xff09;即可進入單用戶模式。 找到類似 linux /boot/vmlinuz-xxx rootU…

Java-75 深入淺出 RPC Dubbo Java SPI機制詳解:從JDK到Dubbo的插件式擴展

點一下關注吧&#xff01;&#xff01;&#xff01;非常感謝&#xff01;&#xff01;持續更新&#xff01;&#xff01;&#xff01; &#x1f680; AI篇持續更新中&#xff01;&#xff08;長期更新&#xff09; AI煉丹日志-30-新發布【1T 萬億】參數量大模型&#xff01;Kim…

【橘子分布式】gRPC(編程篇-上)

一、簡介 我們之前學習了grpc的一些理論知識&#xff0c;現在我們開始正式進入編程環節。 我們的項目結構和之前的thrift結構還是一樣的&#xff0c;一個common,一個client&#xff0c;一個server。只不過在grpc這里common它一般叫做api模塊。還是放置一些公共的實體類&#x…

IOS 18下openURL 失效問題

突然有一天有玩家反饋說應用打開外部連接打不開了&#xff0c;于是查了一下&#xff0c;報錯&#xff1a;BUG IN CLIENT OF UIKIT: The caller of UIApplication.openURL(_:) needs to migrate to the non-deprecated UIApplication.open(_:options:completionHandler:). Force…

前端面試題(React 與 Vue)

目錄 一、React 函數組件 Fiber架構 組件重新渲染 組件通信 為什么不能在if中使用hook useEffect與useLayoutEffect區別 性能優化hooks 受控組件與非受控組件 redux與zustand區別 二、Vue vue2與vue3區別 生命周期 computed與watch區別 v-if與v-show區別 v-mod…

大模型格式

目錄 大模型格式&#xff1a; ollma 可以加載gguf ChatGPT 說&#xff1a; &#x1f50d; 什么是 GGUF&#xff1f; 大模型格式&#xff1a; Ollama 模型格式只能運行已打包成 .gguf 格式的模型&#xff0c;或通過其 Modelfile 方式構建 ModelScope 模型格式大多使用 Hug…

數據結構 棧(1)

1. 棧的概念和結構之前幾篇我們分別講解了順序表和單鏈表的內容&#xff0c;今天我們又來學習一個新的關于數據結構的內容--- 棧 。棧&#xff1a;棧也屬于線性表 , 但它是一種特殊的線性表&#xff0c;其只允許在固定的一端進行插入和刪除元素操作。進行數據插入和刪除操作的一…

【Android代碼】繪本翻頁時通過AI識別,自動通過手機/pad朗讀繪本

核心功能&#xff1a; 打開攝像頭&#xff08;可支持外接攝像頭&#xff09;檢測翻頁&#xff08;后續考慮添加圖像差異算法&#xff09;拍照后用 識圖自動用 TextToSpeech 朗讀文字內容 &#x1f4cc; 說明&#xff1a;使用了 CameraX&#xff08;Android Jetpack&#xff09;…

園區IPv6規劃與部署

?今天我將圍繞“園區IPv6規劃與部署”這一主題&#xff0c;結合行業趨勢、技術難點和實際案例&#xff0c;與大家分享一套可落地的規劃方法論。?在開始前&#xff0c;我想先問大家一個問題&#xff1a;?如果現在讓你給一個新建園區設計網絡&#xff0c;你會優先考慮IPv4還是…

mingw11.2+opencv4.12 cmake contrib編譯

第一次Configure之后&#xff0c;會出現不少錯誤&#xff0c;主要是因為文件沒辦法正常下載引起的,因為之前編譯過vs2022 ,緩存里面有應該下載的文件了&#xff0c;所以這次沒有錯誤&#xff0c;如果你第一次Configure有下載錯誤&#xff0c;可以下載以下的文件飛書 Docs Link:…

免費MCP服務:Excel CSV 轉 JSON MCP by WTSolutions 文檔

簡介 Excel 轉 JSON MCP&#xff08;模型上下文協議&#xff09;提供了一個標準化接口&#xff0c;用于通過模型上下文協議將 Excel 和 CSV 數據轉換為 JSON 格式。此 MCP 實現提供了兩個專門用于數據轉換的工具&#xff1a; excel_to_json_mcp_from_data&#xff1a;轉換制表…

應用集成體系深度解析:從數據互通到流程協同

一、應用集成核心概念框架 #mermaid-svg-0V3XAJsofKi2qCa7 {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-0V3XAJsofKi2qCa7 .error-icon{fill:#552222;}#mermaid-svg-0V3XAJsofKi2qCa7 .error-text{fill:#552222;s…

深入解析 AWS RDS Proxy

在當今微服務架構與無服務器計算快速發展的背景下&#xff0c;數據庫連接成為許多應用系統的性能瓶頸。傳統RDS實例在處理大量短連接請求時&#xff0c;往往面臨連接資源耗盡、連接建立耗時過高等問題。為了解決這一挑戰&#xff0c;AWS 推出了 RDS Proxy 服務&#xff0c;通過…

深度剖析 TDMQ RabbitMQ 版經典隊列底層存儲機制

導語 RabbitMQ 作為開源消息隊列的標桿產品&#xff0c;憑借靈活的路由機制與高可用設計&#xff0c;支撐著海量業務場景的消息流轉。而經典隊列&#xff08;Classic Queue&#xff09; 作為 RabbitMQ 最基礎、應用最廣泛的隊列類型&#xff0c;其底層存儲機制直接決定了消息處…

Spring AI開發智能客服(Tool calling)

文章目錄前言1 思路分析2 工程結構搭建1_數據庫表2_引入依賴3_基礎代碼3 定義 Tool1_分析查詢條件2_定義Function4 系統提示詞5 配置ChatClient6 編寫Controller7 測試8 Tool calling 底層組件1_ToolCallback2_ToolDefinition3_ToolCallingManager4_ResultConverter5_ToolConte…

設計模式筆記_結構型_適配器模式

1.適配器模式介紹適配器模式是一種結構型設計模式&#xff0c;它允許不兼容的接口協同工作。適配器模式的核心思想是將一個類的接口轉換成客戶期望的另一個接口&#xff0c;使得原本由于接口不兼容而不能一起工作的類可以一起工作。你可以將其想象成一個“轉換插頭”——假設你…