在 Java 開發中,若需實現類似 Excel 的編程操作(如讀寫儲存格、設定公式、保護工作表等),透過 JCOM (Java-COM Bridge) 呼叫 Microsoft Excel 的 COM 介面是一種經典且強大的方案。本文將系統整理 JCOM 搭配 JXL 使用的關鍵步驟、核心 API 呼叫流程與資源釋放最佳實踐,助你快速構建穩健的 Excel 自動化工具類。
專案啟動前,請確認以下檔案已就緒:
jdom-1.0.jar、jxl-2.5.5.jar、jcom-2.2.4.jar,放置於專案 lib 目錄。jcom.dll 必須置於 JDK/bin 目錄(推薦)或系統 Windows 目錄,否則執行期會拋出 UnsatisfiedLinkError。所有 JCOM 回傳物件皆為IDispatch型別,操作過程需捕獲JComException。
if (path.endsWith(".xls")) { /* Excel 97-2003 */ }
else if (path.endsWith(".xlsx")) { /* Excel 2007+,需改用 Apache POI */ }
ReleaseManager rm = new ReleaseManager();
IDispatch xlsApp = new IDispatch(rm, "Excel.Application");
ReleaseManager:管理所有 COM 元件生命週期的容器,負責最終資源釋放。IDispatch:COM 物件的通用代理,所有 Excel 物件(Application、Workbook、Worksheet、Range 等)皆以此型別呈現。IDispatch workbooks = (IDispatch) xlsApp.get("Workbooks");
xlsApp.put("Visible", new Java.lang.Boolean(false)); // 背景執行,不彈出視窗
IDispatch workbook = (IDispatch) workbooks.method("Open", new Object[]{ filePath });
File outFile = new File(outPath);
if (outFile.exists()) outFile.delete();
workbook.method("SaveAs", new Object[]{ outPath, new Integer(9) }); // 9 = xlWorkbookNormal (.xls)
IDispatch sheets = (IDispatch) workbook.get("Sheets");
int sheetCount = Integer.parseInt(sheets.get("Count").toString());
for (int i = 1; i <= sheetCount; i++) {
IDispatch sheet = (IDispatch) sheets.get("Item", new Object[]{ new Integer(i) });
String name = sheet.get("Name").toString();
// 或直接用名稱取得:sheets.get("Item", new Object[]{ sheetName })
}
IDispatch activeSheet = (IDispatch) xlsApp.get("ActiveSheet");
int totalRows = Integer.parseInt(((IDispatch)sheet.get("Rows")).get("Count").toString()); // 65536 (Excel 2003)
int totalCols = Integer.parseInt(((IDispatch)sheet.get("Columns")).get("Count").toString()); // 256
IDispatch usedRange = (IDispatch) sheet.get("UsedRange");
int usedRows = Integer.parseInt(((IDispatch)usedRange.get("Rows")).get("Count").toString());
int usedCols = Integer.parseInt(((IDispatch)usedRange.get("Columns")).get("Count").toString());
for (int r = 1; r <= usedRows; r++) {
for (int c = 1; c <= usedCols; c++) {
IDispatch cell = (IDispatch) sheet.get("Cells", new Object[]{ new Integer(r), new Integer(c) });
String val = cell.get("Value").toString(); // 數值會帶 .0 (如 1.0)
String txt = cell.get("Text").toString(); // 顯示文字
boolean hasF = Boolean.parseBoolean(cell.get("HasFormula").toString());
String formula = cell.get("FormulaR1C1").toString(); // R1C1 參考風格公式
}
}
注意:Excel COM 索引皆從 1 起算;Value回傳 Double 字串,若需原始顯示請用Text。
// 保護:密碼、保護繪圖物件、保護內容
activeSheet.method("Protect", new Object[]{ password, true, true });
// 解除保護
activeSheet.method("Unprotect", new Object[]{ password });
若未正確釋放,EXCEL.EXE 將殘留於作業系統進程中。請務必在 finally 區塊執行:
try {
// ... 業務邏輯
} finally {
if (xlsApp != null) {
// 標記已儲存,避免彈出「是否儲存」對話框阻塞進程
IDispatch activeWb = (IDispatch) xlsApp.get("ActiveWorkbook");
if (activeWb != null) activeWb.put("Saved", Boolean.TRUE);
xlsApp.method("Quit", null); // 關閉整個 Excel 應用程式
xlsApp = null;
}
// 釋放所有 COM 參考計數器
if (rm != null) {
rm.release();
rm = null;
}
// 建議輔助 GC
System.gc();
}
workbook.method("Close", null):僅關閉該工作簿,Excel 進程仍在。xlsApp.method("Quit", null):退出 Excel 應用程式,進程結束。Value,顯示/匯出用 Text。FormulaR1C1 回傳 R1C1 風格,若需 A1 風格請用 Formula。Range.Value = Object[][] 批次讀寫。JCOM 橋接 Java 與 Excel COM 介面,雖能發揮 Excel 完整功能(公式計算、圖表、列印、VBA 互動等),但部署依賴 Windows 環境與 Excel 安裝,且效能與穩定性受限於進程間通訊。現代專案若無「必須呼叫 Excel 內建引擎」的剛性需求,推薦改用 Apache POI(支援 .xls/.xlsx、純 Java、跨平台、效能優異)。若維護舊系統或需特殊 COM 功能,本文提供的資源管理模式可確保進程乾淨退出,避免伺服器記憶體洩漏。