读取.xlsx文件必须用xssfworkbook而非hssfworkbook;getcell()返回null需先判空再取值;中文乱码、日期变数字、公式不计算分别需指定locale、用dateutil判断、主动求值;大文件应改用streamingreader或sax模式。

读取 .xlsx 文件必须用 XSSFWorkbook,不是 HSSFWorkbook
Apache POI 对 Excel 格式有严格区分:HSSFWorkbook 只支持旧版 .xls(二进制格式),而 XSSFWorkbook 才能读取新版 .xlsx(OOXML 格式)。用错类会直接抛出 InvalidFormatException 或静默读空。
实操建议:
- 优先用
WorkbookFactory.create(InputStream)自动识别格式,避免手动选错类 - 如果确定是
.xlsx,显式用new XSSFWorkbook(InputStream),但记得关流 - 别在循环里反复 new
XSSFWorkbook——它内存开销大,容易 OOM
getCell() 返回 null 的真实原因和安全取值方式
getCell() 不返回空字符串或默认值,而是返回 null:要么该单元格真没内容,要么是空单元格被 Excel 优化掉了(尤其在中间列全空时)。
常见错误现象:
立即学习“Java免费学习笔记(深入)”;
-
cell.getStringCellValue()报NullPointerException - 读到的行数远少于 Excel 显示行数
正确做法:
- 先判断
cell != null,再调用getCellType()区分字符串、数字、日期等类型 - 统一用
DataFormatter转换值,避免getNumericCellValue()错转为科学计数法字符串 - 遍历行时用
row.getLastCellNum()而非row.getPhysicalNumberOfCells(),后者只统计非空单元格
中文乱码、日期变数字、公式不计算的三类典型问题
这些不是 POI bug,而是 Excel 文件本身元数据或读取策略没对齐。
具体应对:
- 中文显示为 “???”:检查文件是否用 UTF-8 编码保存(其实 Excel 本身不存编码,但某些导出工具会写错 BOM);
DataFormatter默认用系统 locale,可显式传new Locale("zh", "CN") - 日期变成一串数字(如 44562.0):这是 Excel 内部序列号,必须用
DateUtil.isCellDateFormatted(cell)判断后,再用cell.getDateCellValue() - 公式单元格显示公式文本而非结果:调用
workbook.getCreationHelper().createFormulaEvaluator().evaluate(cell)主动求值,否则默认只读原始内容
大文件(>10MB)读取卡顿甚至 OOM 怎么办
XSSFWorkbook 是 DOM 模式,整份文件加载进内存;10 万行以上就容易撑爆堆空间。
关键解法只有两个:
- 改用
StreamingReader(POI 5.2+)或SAXParser模式(OPCPackage+XSSFReader),牺牲部分 API 简洁性换内存可控性 - 确保 JVM 启动参数加
-Xmx2g之类,且关闭不必要的日志(poi-ooxml-schemas依赖巨大,若不用高级图表可排除 - 别在
for (Row row : sheet)里做耗时操作(如 DB 插入),先缓存结构化数据再批量处理
真正麻烦的从来不是“怎么读”,而是“读完怎么不崩”。小文件图省事用 WorkbookFactory,大文件必须直面 SAX 和事件驱动——这点绕不开。










