Java 操作 Office 文件主流方案:Apache POI(純 Java、不需安裝 Office、功能受限)、docx4j(OpenXML 操作)、JACOB(Java-COM Bridge、呼叫真實 Word COM 物件、功能最完整、需 Windows+Office)。本文聚焦 JACOB 方案。
jacob.dll 相容性jacob.jar + jacob.dll (32/64 位元對應 JVM)System32、java.library.path 或專案 -Djava.library.path=./lib| Word 物件 | JACOB 取得方式 | 用途 |
|---|---|---|
| Application | new ActiveXComponent("Word.Application") | Word 應用程式實例 |
| Documents | word.getProperty("Documents").toDispatch() | 文件集合 |
| Document | Dispatch.invoke(docs, "Open", ...) | 單一文件 |
| Selection | word.getProperty("Selection").toDispatch() | 目前選取範圍/游標 |
| Range | Dispatch.get(selection, "Range") | 連續文件區域 |
| Hyperlinks | Dispatch.get(doc, "Hyperlinks") | 超連結集合 |
| Tables | Dispatch.get(doc, "Tables") | 表格集合 |
| Rows/Cells | Dispatch.get(table, "Rows") | 列/儲存格集合 |
ActiveXComponent word = new ActiveXComponent("Word.Application");
Dispatch docs = word.getProperty("Documents").toDispatch();
Dispatch doc = Dispatch.invoke(docs, "Open", Dispatch.Method,
new Object[] { filePath, new Variant(true), new Variant(false) }, // 轉換確認、唯讀
new int[1]).toDispatch();
Dispatch selection = word.getProperty("Selection").toDispatch();
// 新增超連結:TextToDisplay 為顯示文字,建議存放欄位標識如 {table.fieldName}
Dispatch hyperlinks = Dispatch.get(doc, "Hyperlinks").toDispatch();
Dispatch range = Dispatch.get(selection, "Range").toDispatch();
Dispatch hyperlink = Dispatch.invoke(hyperlinks, "Add", Dispatch.Method,
new Object[] {
range,
new Variant("Address"), // Address 存檔時可能被轉絕對路徑,不建議存業務資料
new Variant(""), // SubAddress
new Variant("{table.fieldName}"), // ScreenTip:建議存業務鍵值
new Variant("顯示文字") // TextToDisplay:使用者可見內容
},
new int[4]).toDispatch();
// 設定格式
Dispatch hyperlinkRange = Dispatch.get(hyperlink, "Range").toDispatch();
Dispatch.call(hyperlinkRange, "Select");
Dispatch font = Dispatch.get(selection, "Font").toDispatch();
Dispatch.put(font, "Underline", new Variant(0)); // 0=無底線
Dispatch.put(font, "Color", new Variant(0x000000)); // 黑色
Dispatch.call(selection, "MoveRight", new Variant(1), new Variant(1)); // 取消選取
// 1. 取得所有表格(含巢狀表格遞迴)
void collectTables(Dispatch container, List list) {
Dispatch tables = Dispatch.get(container, "Tables").toDispatch();
int count = Dispatch.get(tables, "Count").toInt();
for (int i = 1; i <= count; i++) {
Dispatch table = Dispatch.invoke(tables, "Item", Dispatch.Method,
new Object[] { new Integer(i) }, new int[1]).toDispatch();
list.add(table);
collectTables(table, list); // 遞迴處理巢狀表格
}
}
// 2. 複製資料列
Dispatch row = getRow(table, templateRowIndex); // 取得範本列
Dispatch.call(row, "Select");
Dispatch.call(selection, "Copy");
for (int i = 1; i < dataCount; i++) {
Dispatch.call(selection, "Paste"); // 在游標處貼上
}
// 3. 遍歷列,替換超連結 TextToDisplay
Dispatch rows = Dispatch.get(table, "Rows").toDispatch();
int rowCount = Dispatch.get(rows, "Count").toInt();
for (int r = 1; r <= rowCount; r++) {
Dispatch dataRow = Dispatch.invoke(rows, "Item", Dispatch.Method,
new Object[] { new Integer(r) }, new int[1]).toDispatch();
Dispatch.call(dataRow, "Select");
Dispatch hyperlinks = Dispatch.get(selection, "Hyperlinks").toDispatch();
int hlCount = Dispatch.get(hyperlinks, "Count").toInt();
for (int h = 1; h <= hlCount; h++) {
Dispatch hl = Dispatch.invoke(hyperlinks, "Item", Dispatch.Method,
new Object[] { new Integer(h) }, new int[1]).toDispatch();
String key = Dispatch.get(hl, "ScreenTip").toString(); // 取得業務鍵值
String value = dataMap.get(key); // 從資料來源取值
if (value != null) {
Dispatch.put(hl, "TextToDisplay", value); // 替換顯示文字
}
}
}
// 4. 移除超連結保留純文字(最終輸出前)
Dispatch.call(hl, "Delete"); // 僅移除連結屬性,文字保留
// 列印預覽
Dispatch.call(doc, "PrintPreview");
// 另存新檔
Dispatch.invoke(doc, "SaveAs", Dispatch.Method,
new Object[] { outputPath, new Variant(16) }, // 16 = wdFormatXMLDocument (docx)
new int[1]);
// 關閉文件與 Word
Dispatch.call(doc, "Close", new Variant(false));
word.invoke("Quit", new Variant[0]);
| 問題 | 原因 | 解決 |
|---|---|---|
| ComFailException: 無法建立 ActiveXComponent | Office 未安裝、版本不符、DLL 位元數錯誤 | 檢查 Office 安裝、確認 jacob.dll 32/64 位元與 JVM 一致 |
| Word 進程殘留不退出 | 未正確呼叫 Quit、例外中斷流程 | finally 區塊強制 Quit、搭配 Runtime.getRuntime().exec("taskkill /im WINWORD.EXE") 當備援 |
| 中文亂碼/方塊字 | 字體缺失、編碼轉換 | 確保目標機有對應字體、JACOB Variant 字串傳遞為 Unicode |
| 效能極差(秒級/操作) | COM 呼叫跨行程封送開銷大 | 批次操作、最小化 Dispatch 呼叫、關閉螢幕更新 word.setProperty("Visible", false) |
| 伺服器端無法使用(無互動桌面) | Windows 服務/IIS 預設 Session 0 隔離,無桌面權限 | 改用 POI/docx4j/LibreOffice headless 模式,JACOB 僅適合桌面應用 |
新專案優先評估:docx4j(OpenXML 直接操作、跨平台、無 Office 相依)、Apache POI(成熟穩定、功能覆蓋 80%+ 需求)、LibreOffice UNO/headless(轉檔、列印、複雜排版)。僅在「必須使用 Word 專有功能(郵件合併、複雜列印佈局、ActiveX 控制項)」且部署於 Windows 桌面環境時,才選擇 JACOB。