在 Java 處理 Excel 的領域中,Apache POI 與 JXL 是兩大主流庫。作為 POI 的長期使用者,我更傾向於 POI,主要因為它對現代 Excel 格式(.xlsx,即 Office 2007+ 版本)支援更完善,且社群活躍、文檔豐富。本文將通過實際代碼示例,詳細講解如何使用 POI 進行單元格樣式深度複製、不同類型數據的讀取策略,以及公式的讀寫操作。
Excel 單元格包含豐富的樣式信息:字體、邊框、填充顏色、對齊方式、數據格式等。在跨 Workbook 複製數據時,必須將樣式完整克隆。以下是一個生產級的樣式複製工具方法,處理了空指針、顏色對象轉換等邊界情況:
public XSSFCellStyle cloneAllCellStyle(XSSFCell sourceCell, XSSFWorkbook targetWb) {
// 1. 在目標工作簿中創建新樣式
XSSFCellStyle tempStyle = targetWb.createCellStyle();
// 2. 處理數據格式(如日期、貨幣、百分比等)
DataFormat format = targetWb.createDataFormat();
try {
tempStyle.setDataFormat(format.getFormat(sourceCell.getCellStyle().getDataFormatString()));
} catch (NullPointerException e) {
tempStyle.setDataFormat((short) 0); // 預設為 General 格式
}
// 3. 深度複製字體屬性
XSSFFont font = targetWb.createFont();
XSSFCellStyle sourceStyle = sourceCell.getCellStyle();
XSSFFont sourceFont = sourceStyle.getFont();
font.setColor(sourceFont.getXSSFColor());
font.setBold(sourceFont.getBold());
font.setBoldweight(sourceFont.getBoldweight());
try {
font.setCharSet(sourceFont.getCharSet());
} catch (POIXMLException e) {
font.setCharSet(0);
}
font.setFamily(sourceFont.getFamily());
font.setFontHeight(sourceFont.getFontHeight());
font.setFontHeightInPoints(sourceFont.getFontHeightInPoints());
font.setFontName(sourceFont.getFontName());
font.setItalic(sourceFont.getItalic());
font.setStrikeout(sourceFont.getStrikeout());
font.setTypeOffset(sourceFont.getTypeOffset());
font.setUnderline(sourceFont.getUnderline());
tempStyle.setFont(font);
// 4. 複製對齊與垂直對齊
tempStyle.setAlignment(sourceStyle.getAlignment());
tempStyle.setVerticalAlignment(sourceStyle.getVerticalAlignment());
// 5. 複製邊框樣式與顏色
tempStyle.setBorderBottom(sourceStyle.getBorderBottom());
tempStyle.setBorderLeft(sourceStyle.getBorderLeft());
tempStyle.setBorderRight(sourceStyle.getBorderRight());
tempStyle.setBorderTop(sourceStyle.getBorderTop());
tempStyle.setBottomBorderColor(sourceStyle.getBottomBorderXSSFColor());
tempStyle.setLeftBorderColor(sourceStyle.getLeftBorderXSSFColor());
tempStyle.setRightBorderColor(sourceStyle.getRightBorderXSSFColor());
tempStyle.setTopBorderColor(sourceStyle.getTopBorderXSSFColor());
// 6. 複製填充圖案與前景/背景色
tempStyle.setFillPattern(sourceStyle.getFillPattern());
tempStyle.setFillBackgroundColor(sourceStyle.getFillBackgroundColorColor());
try {
tempStyle.setFillForegroundColor(sourceStyle.getFillForegroundColorColor());
} catch (NullPointerException e) {
tempStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
}
// 7. 其他屬性:旋轉、隱藏、換行、縮進、鎖定
tempStyle.setRotation(sourceStyle.getRotation());
tempStyle.setHidden(sourceStyle.getHidden());
tempStyle.setWrapText(sourceStyle.getWrapText());
tempStyle.setIndention(sourceStyle.getIndention());
tempStyle.setLocked(sourceStyle.getLocked());
return tempStyle;
}
使用提示:調用時傳入來源單元格與目標 Workbook 即可獲得獨立的樣式對象,避免修改影響原樣式。
POI 中單元格類型由 cell.getCellType() 返回(POI 4.0+ 建議使用 cell.getCellTypeEnum())。不同類型需調用對應的 getter 方法,否則會拋出異常。以下是標準的類型分發邏輯:
List<Object> tempValue = new ArrayList<>();
Map<String, Object> map = new HashMap<>();
switch (cell.getCellType()) {
case Cell.CELL_TYPE_BLANK:
tempValue.add("");
break;
case Cell.CELL_TYPE_BOOLEAN:
tempValue.add(cell.getBooleanCellValue());
break;
case Cell.CELL_TYPE_ERROR:
tempValue.add(cell.getErrorCellString());
break;
case Cell.CELL_TYPE_FORMULA:
// 關鍵:讀取公式字符串而非計算結果
tempValue.add(cell.getCellFormula());
map.put("formulaFlag", true); // 標記該單元格包含公式
break;
case Cell.CELL_TYPE_NUMERIC:
// 進階:可結合 DateUtil.isCellDateFormatted 判斷日期類型
tempValue.add(cell.getNumericCellValue());
break;
case Cell.CELL_TYPE_STRING:
tempValue.add(cell.getStringCellValue());
break;
default:
break;
}
最佳實踐:對於數值類型,建議額外檢查 DateUtil.isCellDateFormatted(cell) 以正確處理日期單元格;公式單元格若需計算結果,可使用 FormulaEvaluator。
導出 Excel 時需按業務邏輯動態構建結構,以下代碼展示核心 API 用法:
// 創建工作簿(.xlsx 格式)
XSSFWorkbook targetWb = new XSSFWorkbook();
// 創建/刪除 Sheet
XSSFSheet targetSheet = targetWb.createSheet("行匯總");
// targetWb.removeSheetAt(index); // index 從 0 開始
// 創建行(i 為行索引)
XSSFRow row = targetSheet.createRow(i + num1 - startRow + 1);
// 創建單元格(j 為列索引)
XSSFCell cell = row.createCell(j);
// 設置值與樣式
cell.setCellValue("示例數據");
cell.setCellStyle(cloneAllCellStyle(sourceCell, targetWb));
POI 的公式處理遵循「讀取公式字符串 → 寫入公式字符串 → Excel 打開時自動計算」的模式:
cell.getCellFormula() 返回不含等號的公式體,如 SUM(A1:B1)。cell.setCellFormula("SUM(A1:B1)"),同樣不需等號。掌握 POI 核心在於理解三大對象模型:Workbook → Sheet → Row → Cell 的層級關係,以及 CellStyle、Font、DataFormat 的組合規則。實際項目中建議封裝通用工具類(如上述樣式複製、類型安全取值),並善用 FormulaEvaluator 處理公式計算、DataFormatter 統一格式化輸出,提升代碼健壯性與可維護性。