| 特性 | .xls (Excel 97-2003) | .xlsx (Excel 2007+) |
|---|---|---|
| 儲存格式 | BIFF (二進位) | OOXML (XML + ZIP 壓縮) |
| 檔頭魔數 | 無固定 | PK\\x03\\x04 (ZIP) |
| 行數上限 | 65,536 (2^16) | 1,048,576 (2^20) |
| 列數上限 | 256 (IV) | 16,384 (XFD) |
| POI 類前綴 | HSSF (Horrible SpreadSheet Format) | XSSF (XML SpreadSheet Format) |
| 通用介面 | Workbook/Sheet/Row/Cell (SS 模型) | |
⚠️ **務必確認目標檔案格式再選擇 API**,混用會拋 InvalidFormatException。
// 自動根據副檔名/檔頭判斷
Workbook wb = WorkbookFactory.create(new FileInputStream("data.xlsx")); // 或 .xls
Sheet sheet = wb.getSheetAt(0);
Row row = sheet.getRow(0);
Cell cell = row.getCell(0);
String val = cell.getStringCellValue();
Workbook wb = new XSSFWorkbook(); // 或 new HSSFWorkbook()
Sheet sheet = wb.createSheet("報表");
String safe = WorkbookUtil.createSafeSheetName("[O'Brien's sales*?]"); // 自動轉義非法字符
Sheet sheet2 = wb.createSheet(safe);
CreationHelper helper = wb.getCreationHelper();
Row row = sheet.createRow(0); // 索引從 0 開始
row.createCell(0).setCellValue(100); // 數值
row.createCell(1).setCellValue(3.14);
row.createCell(2).setCellValue(helper.createRichTextString("中文內容")); // 富文本
row.createCell(3).setCellValue(true); // 布林
row.createCell(4).setCellValue(new Date()); // 日期 (需配合 CellStyle)
CellStyle dateStyle = wb.createCellStyle();
dateStyle.setDataFormat(helper.createDataFormat().getFormat("yyyy-MM-dd HH:mm:ss"));
Cell dateCell = row.createCell(4);
dateCell.setCellValue(new Date());
dateCell.setCellStyle(dateStyle);
try (FileOutputStream out = new FileOutputStream("report.xlsx")) {
wb.write(out);
}
try (InputStream in = new FileInputStream("report.xlsx")) {
Workbook wb = WorkbookFactory.create(in);
Sheet sheet = wb.getSheetAt(0);
for (Row row : sheet) {
if (row == null) continue;
for (Cell cell : row) {
if (cell == null) continue;
switch (cell.getCellType()) {
case STRING: System.out.print(cell.getStringCellValue() + "\t"); break;
case NUMERIC:
if (DateUtil.isCellDateFormatted(cell))
System.out.print(cell.getDateCellValue() + "\t");
else
System.out.print(cell.getNumericCellValue() + "\t");
break;
case BOOLEAN: System.out.print(cell.getBooleanCellValue() + "\t"); break;
case FORMULA: System.out.print(cell.getCellFormula() + "\t"); break;
default: System.out.print("\t");
}
}
System.out.println();
}
}
XSSF 全載入記憶體,百萬行會 OOM。SXSSF 僅保留最近 N 行在記憶體,其餘刷盤。
SXSSFWorkbook wb = new SXSSFWorkbook(100); // 保留 100 行
// 後續 API 與 XSSF 完全一致
wb.write(out);
wb.dispose(); // 必須調用釋放臨時檔案
| 問題 | 原因 | 解決 |
|---|---|---|
| 數字讀成科學計數法 | NUMERIC 型別預設 toString | 用 DataFormatter 或 DecimalFormat 格式化 |
| 日期讀成數字 | Excel 日期本質是數字 | DateUtil.isCellDateFormatted(cell) 判斷後 getDateCellValue() |
| 公式值為 null | POI 不自動計算公式 | FormulaEvaluator evaluator = wb.getCreationHelper().createFormulaEvaluator(); evaluator.evaluate(cell) |
| 中文亂碼 | 編碼不一致 | 確保檔案 UTF-8,POI 內部用 Unicode,通常無問題 |
| 合併儲存格讀取 | 只在主儲存格有值 | sheet.getMergedRegions() 遍歷區域 |
| SXSSF 寫完檔案為空 | 未調用 dispose() | 必須在 write() 後調用 dispose() |
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>5.2.3</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>