上傳 .xlsx 文件解析時拋出:
org.apache.poi.poifs.filesystem.OfficeXmlFileException:
The supplied data appears to be in the Office 2007+ XML.
You are calling the part of POI that deals with OLE2 Office Documents.
You need to call a different part of POI to process this data (eg XSSF instead of HSSF)
根因:代碼硬編碼使用 HSSFWorkbook(僅支持 .xls OLE2 格式),但文件實為 .xlsx (OOXML 格式),需用 XSSFWorkbook。
// 核心一行代碼:自動識別 .xls / .xlsx / .xlsm 等格式
Workbook wb = WorkbookFactory.create(inputStream);
WorkbookFactory.create() 內部通過文件頭魔數(Magic Number)判斷格式,返回統一 Workbook 介面,屏蔽 HSSF/XSSF 差異。
<properties>
<poi.version>5.3.0</poi.version> <!-- 統一版本,避免傳遞依賴衝突 -->
</properties>
<dependencies>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi</artifactId>
<version>${poi.version}</version>
</dependency>
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>${poi.version}</version>
</dependency>
<!-- poi-ooxml-schemas 5.0.0+ 已合併入 poi-ooxml,無需單獨引入 -->
</dependencies>
版本建議:使用 5.x 系列(如 5.3.0),修復大量安全漏洞、性能優化、支持最新 Excel 特性。舊版 3.14-beta1 已停止維護。
@Service
public class ExcelParseService {
public List parse(MultipartFile file) throws IOException {
// 1. 基礎校驗
String filename = file.getOriginalFilename();
if (filename == null || !(filename.endsWith(".xls") || filename.endsWith(".xlsx"))) {
throw new IllegalArgumentException("僅支持 .xls/.xlsx 格式");
}
// 2. 統一入口解析
try (InputStream is = file.getInputStream();
Workbook wb = WorkbookFactory.create(is)) {
Sheet sheet = wb.getSheetAt(0); // 默認首張表
return parseSheet(sheet);
}
}
private List parseSheet(Sheet sheet) {
List list = new ArrayList<>();
for (Row row : sheet) {
if (row.getRowNum() == 0) continue; // 跳過表頭
Customer c = new Customer();
c.setName(getCellString(row.getCell(0)));
c.setPhone(getCellString(row.getCell(1)));
c.setEmail(getCellString(row.getCell(2)));
list.add(c);
}
return list;
}
private String getCellString(Cell cell) {
if (cell == null) return "";
return switch (cell.getCellType()) {
case STRING -> cell.getStringCellValue();
case NUMERIC -> DateUtil.isCellDateFormatted(cell)
? cell.getLocalDateTimeCellValue().toString()
: String.valueOf((long) cell.getNumericCellValue());
case BOOLEAN -> String.valueOf(cell.getBooleanCellValue());
case FORMULA -> cell.getCellFormula();
default -> "";
};
}
}
| 坑點 | 現象 | 解決 |
|---|---|---|
| 大文件 OOM | WorkbookFactory 全量載入內存 | 用 SXSSFWorkbook (流式寫入) 或 XSSFReader (SAX 事件流式讀取) |
| 數字儲存格讀成科學計數法 | getNumericCellValue() 返回 double | 用 DataFormatter 格式化輸出,或判斷類型轉 long |
| 日期類型誤判 | NUMERIC 儲存格實為日期 | 必須調用 DateUtil.isCellDateFormatted(cell) 判斷 |
| 公式單元格報錯 | 未計算公式值 | 調用 FormulaEvaluator.evaluateAll(wb) 預計算 |
| .xls 文件實為 HTML/CSV | WorkbookFactory 拋異常 | 前置校驗文件頭魔數,或捕獲異常降級嘗試其他解析器 |
WorkbookFactory.create() 是 POI 解析 Excel 的黃金單一入口,屏蔽了 OLE2 (.xls) 與 OOXML (.xlsx) 的底層差異。配合統一版本依賴、流式讀取大文件策略、DataFormatter 統一單元格輸出,構建健壯的 Excel 導入服務。