使用Apache POI的HSSFWorkbook和XSSFWorkbook类可以高效读取Excel文件,支持.xls和.xlsx格式,同时通过用户模式或事件模式灵活应对不同数据量场景。 这套方案被广泛应用于Java开发中的数据导入、报表处理等环节,关键在于根据文件大小和内存限制选择正确的读取策略。
poi读取excel的步骤:从入门到实战
对于初次接触Java开发的同学来说,poi读取excel的步骤其实可以拆解为四个环节:环境准备、创建Workbook、遍历行与单元格、以及异常处理。
添加Maven依赖并配置版本
在pom.xml中加入以下依赖,就能同时支持.xls和.xlsx格式:
- poi-ooxml(包含XSSF和HSSF)
- poi-ooxml-schemas(用于解析XML)
<dependency>
<groupId>org.apache.poi</groupId>
<artifactId>poi-ooxml</artifactId>
<version>5.2.3</version>
</dependency>
据Apache POI官方文档,5.x版本对内存占用和性能做了明显优化,推荐使用。
根据文件后缀选择Workbook实现
- HSSFWorkbook:处理.xls格式(Excel 97-2003),最大支持65536行。
- XSSFWorkbook:处理.xlsx格式,行数上限大幅提升,但内存消耗也更高。
- SXSSFWorkbook:低内存版本,适合写大数据文件,读取时用户模式不适用。
读取时直接通过WorkbookFactory.create(inputStream)自动判断格式,无需手动区分。
遍历行和单元格的核心操作
Workbook workbook = WorkbookFactory.create(new FileInputStream("data.xlsx"));
Sheet sheet = workbook.getSheetAt(0);
for (Row row : sheet) {
for (Cell cell : row) {
switch (cell.getCellType()) {
case STRING: System.out.print(cell.getStringCellValue()); break;
case NUMERIC: System.out.print(cell.getNumericCellValue()); break;
// 其他类型同理
}
}
}
workbook.close();
这段代码是java poi读取excel教程中最常见的示例,但实际开发中需要处理空行、空单元格以及公式计算,后面会展开说明。
处理空行和空单元格
poi读取excel空行问题经常出现在数据中间有空白行或合并单元格时,默认情况下,sheet.iterator()会跳过空行,但如果你需要保留行号,可以用sheet.getRow(rowNum)并判断null,对于空单元格,cell.getCellType()会返回BLANK,统一处理即可。
poi读取excel大文件慢怎么办?试试事件驱动模式
当文件超过10MB甚至百MB级别,用户模式(将整个文件加载到内存)会造成内存溢出或严重卡顿。poi读取excel大文件慢的问题根源在于XSSFWorkbook会一次性解析所有XML到对象中。
用户模式与事件模式对比
| 特性 | 用户模式(UserModel) | 事件模式(EventModel) |
|---|---|---|
| 内存占用 | 高,整个文件在内存 | 低,流式处理,逐行解析 |
| 使用复杂度 | 简单,直接遍历 | 较高,需要实现回调接口 |
| 适用场景 | 文件小于10MB,内存充足 | 超大文件,内存受限 |
| 支持格式 | .xls和.xlsx | .xlsx专用(SAX),.xls可用HSSFListener |
行业共识认为,事件模式是处理大Excel的唯一可靠方案,Apache POI提供了XSSFReader和XSSFSheetXMLHandler,底层使用SAX解析器,只读取当前行数据,内存占用稳定在几MB内。
实战:基于XSSFSheetXMLHandler的事件驱动读取
- 创建XSSFReader实例,获取SharedStringsTable和StylesTable。
- 实现SheetContentsHandler接口,在startRow、cell、endRow等回调中处理数据。
- 用XMLReader解析每个Sheet的XML内容。
XSSFReader reader = new XSSFReader(opcPackage);
SharedStringsTable sst = reader.getSharedStringsTable();
XMLReader parser = SAXHelper.newXMLReader();
parser.setContentHandler(new SheetHandler(sst));
for (InputStream sheet : reader.getSheetsData()) {
parser.parse(sheet);
}
这种方式下,poi读取excel大文件慢的问题迎刃而解,速度不降反升,因为省去了对象构建开销。
实战中常见问题与优化策略
即使遵循了标准步骤,实际开发中仍会遇到各种边界情况,下面汇总了几类高频问题,并给出具体解决思路。
poi读取excel公式结果
为公式时,getCellType()返回FORMULA,需要先调用evaluateFormulaCell()获取缓存结果,或者使用FormulaEvaluator实时计算:
FormulaEvaluator evaluator = workbook.getCreationHelper().createFormulaEvaluator(); CellValue cellValue = evaluator.evaluate(cell);
大数据量下的内存优化
- 使用SXSSFWorkbook读取时,设置accessWindowSize限制内存中行数。
- 读取.xlsx时优先使用事件模式,避免构建XSSFWorkbook。
- 对于.xls,HSSFWorkbook在行数较多时也会占用大量内存,建议改用HSSFListener流式处理。
多线程读取Sheet
POI的Workbook不是线程安全的,但可以并行读取多个Sheet:
- 每个线程创建独立的Workbook实例(只读)。
- 或者使用XSSFReader的getSheetsData()返回多个InputStream,并行解析。
poi和easyexcel哪个好?从场景出发做选择
在Java社区,poi和easyexcel哪个好是开发者经常争论的话题,两者都是Apache POI的衍生方案,但定位不同。
对比维度
| 维度 | Apache POI | EasyExcel |
|---|---|---|
| 开发团队 | Apache基金会,社区庞大 | 阿里巴巴,聚焦大数据读写 |
| 功能完整度 | 极高,支持Word、PPT等 | 仅Excel,读写分离 |
| 学习曲线 | 较陡,API偏底层 |
低,注解驱动,自动处理类型 |
| 内存占用(读) | 用户模式高,事件模式低 | 默认SAX模式,内存极低 |
| 版本更新 | 稳定,但新特性推进慢 | 迭代快,与阿里生态集成好 |
选择建议
- 如果你需要处理Word、PPT等其他Office格式,或者系统已有POI依赖,poi读取excel的步骤已经熟悉,继续用POI即可。
- 如果项目是纯Excel场景,且文件普遍较大,EasyExcel的注解和低内存优势更明显,行业共识认为,EasyExcel在读取大文件时比原生POI用户模式快30%以上,但事件模式下两者差异不大。
- 注意:EasyExcel底层依然依赖POI的ooxml-schemas,版本冲突时需要谨慎。
无论是选择Apache POI还是EasyExcel,核心都是理解文件格式和内存模型,对于日常开发,poi读取excel的步骤只需要掌握用户模式配合事件模式,就能覆盖95%以上场景,根据文件大小选模式,根据团队习惯选框架,没有绝对最优,只有最适合。
关于POI读取Excel的常见问题解答
poI读取excel时如何保留日期格式?
单元格存储日期实际上是数值,需要判断单元格格式是否为日期格式,POI提供了DateUtil.isCellDateFormatted(cell)方法,返回true时调用cell.getDateCellValue()即可,注意,部分自定义格式可能无法识别,需要手动匹配格式字符串。
poI读取excel怎么跳过标题行?
在遍历行时,直接调用sheet.getRow(rowNum)并判断rowNum是否大于等于标题行索引,或者使用row.getRowNum()与预设值比较,事件模式下,在startRow回调中判断行号,直接跳过。
读取excel时字符编码问题如何解决?
POI内部已按UTF-8处理.xlsx,但读取.xls时可能出现乱码,确保JVM默认编码为UTF-8,或者使用new String(value.getBytes(“ISO-8859-1”), “UTF-8”)转换,对于SharedStringsTable中的字符串,POI已经做了编码处理,极少出现问题。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/504296.html



