说一下你了解的POI和EasyExcel
POI(Poor Obfuscation Implementation):它是 Apache 软件基金会的一个开源项目,为 Java 程序提供了读写 Microsoft Office 格式文件的功能,支持如 Excel、Word、PowerPoint 等多种文件格式,是 Java 操作 Office 文档的基础工具,发展历史悠久,功能强大且全面
EasyExcel :是阿里巴巴开源的一个基于 POI 进行二次封装的轻量级 Excel 处理框架 ,主要专注于 Excel 文件的读写操作,简化了 POI 的操作流程
EasyExcel对比POI
功能丰富度
POI :POI 是一个底层的 Java 操作 Excel 的工具包,在进行读写操作时,需要手动处理文件流、解析器、工作表、行和单元格等多个元素
以读取文件为例,需要创建工作簿对象、获取工作表 、遍历行和单元格等,代码量较多且逻辑复杂。
对于复杂的操作,如设置单元格样式、合并单元格等,还需要编写更多的代码来实现。这使得使用 POI 进行开发时,代码的复杂度显著增加
EasyExcel:
EasyExcel 对常见的 Excel 读写场景进行了高度封装,提供了极为简洁的 API。在读取 Excel 时,只需定义与表格列对应的实体类和监听器
内存占用
EasyExcel 采用基于事件驱动 的读写模式,在读写大数据量的 Excel 文件时,逐行处理数据 ,不需要将整个文件加载到内存中 。这使得它在处理大规模数据时,内存占用非常低,能够有效避免内存溢出的问题。即使处理包含数百万行数据的 Excel 文件,也能稳定运行
POI 的用户模型 API(如 HSSF
和 XSSF
)在处理 Excel 文件时,会将整个文件加载到内存中 ,当文件数据量较大时,会占用大量的内存资源,容易导致内存溢出。虽然 POI 也提供了事件模型 API(如 SAX
解析器)来处理大数据量文件,但使用起来相对复杂,需要开发人员具备较高的技术水平
读写性能
POI :POI 的用户模型 API 在处理大数据量文件时,由于需要将整个文件加载到内存中,会导致读写效率降低。而事件模型 API 虽然在处理大数据量时性能较好,但需要手动处理事件,开发难度较大,并且在处理一些复杂操作时,效率也会受到一定影响
EasyExcel:由于采用了事件驱动的方式,EasyExcel 在读写数据时,数据处理和文件操作是同步进行的,减少了中间环节,提高了读写效率。特别是在处理大数据量文件时,其逐行处理的方式能够更快地完成读写操作
在处理大数据量的 Excel 文件时,EasyExcel 采用了基于事件驱动的读写模式,逐行处理数据,避免了将整个文件加载到内存中,从而大大降低了内存占用。例如,当需要读取一个包含数百万行数据的 Excel 文件时,使用 POI 的用户模型 API 可能会导致内存溢出,而 EasyExcel 可以稳定地处理这些数据
事件驱动为什么快+安全?
EasyExcel 通过 SAX 事件驱动 + 流式读写 ,实现了:
✅ 超低内存占用 (恒定内存,与文件大小无关)。
✅ 高效处理大文件 (GB 级 Excel 轻松应对)。
✅ 避免 OOM(彻底解决 POI 的内存瓶颈)
为什么说EasyExcel的OOM风险小
EasyExcel不需要将整个文件加载到内存中,poi需要加载整个文件?
1. 传统 POI 的 DOM 模式:全量加载
DOM(Document Object Model) :
POI 将整个 Excel 文件解析为一个树形结构对象 ,全部加载到内存中。
类似于把一本书全部复印到脑子里,再逐页阅读
2. EasyExcel 的 SAX 模式:流式逐行处理
SAX(Simple API for XML) :
EasyExcel 像流水线一样逐行扫描 Excel 文件,触发事件回调处理数据。
类似于用扫描仪逐页扫描一本书,读一页处理一页,然后立刻扔掉
EasyExcel 的 SAX 模式 通过事件驱动 + 即时丢弃数据(即时刷盘) ,实现了按需加载 ,而 POI 的 DOM 模式 为支持复杂功能必须全量加载
EasyExcel如何避免写入OOM?
POI必须是全量写入,这也是它的内存瓶颈
而EasyExcel 默认每处理 2000 行 (可配置)就将数据从内存刷入磁盘文件流,即时释放内存 。
内存占用 ≈ 单批次数据大小(而非全量数据)。例如写入sheet1然后释放sheet1,再写入sheet2,而不用sheet1,sheet2必须一起写入,分sheet页写可以实现资源及时释放
EasyExcel为啥不维护了?对此你怎么看?
核心功能已成熟,暂无重大特性新增需求。以修复 Bug 和兼容性更新为主,而非频繁发布新功能
开发者资源转移,阿里内部可能将资源投入其他更高优先级项目(如 Spring Cloud Alibaba),开源团队依赖社区贡献,但核心开发者时间有限
面试回答思路引导
面试回答
poi是apache的开源项目,功能非常丰富,而EasyExcel是基于poi的二次简化封装,简化了poi的操作,方便使用
主要让写入和写出更加简单,没必要去操作很多的细节,例如不需要手动编写输入输出流,手动关闭输入输入流的资源,编写解析器等
EasyExcel只需要只需定义与表格列对应的 实体类 和 监听器
EasyExcel主要解决的poi的内存瓶颈和读取效率问题
poi使用的是传统的全量加载,而EasyExcel是流失加载,也就是事件驱动+及时刷盘
poi读取的时候,必须要把整个文件加载到内存中读取
poi写入的时候必须一次性全量写入
而EasyExcel读取的时候是流式读取,逐行扫描,读取完的数据从内存中扔掉,不占用内存
EasyExcel写入的时候是流式写入,我们分sheet页写入,这个sheet页写入完就及时释放资源,实现及时刷盘不占用内存
这是他们的区别,也是基于poi的重点优化,poi因为是全量读取和写入逻辑所以oom的概率会非常大
EasyExcel是写完sheet页就释放不会一直占用内存,所以oom的概率小,但这并不说明不会出现oom问题了,使用EasyExcel的时候还是需要严格控制sheet页的,因为sheet页是存到一个List<>中的,如果sheet页过大仍然会导致oom问题
常用的方法
EasyExcel.read() 读
EasyExcel.write() 写
WriteSheet sheet = EasyExcel.writerSheet("批次").build() 构造sheet页
ExcelWriter writer = EasyExcel.write(filePath).build() 构造用来写入的ExcelWriter
writer.write(excels, sheet) 把Sheet页放到ExcelWriter里面,然后写入我们传入的内容excels列表
@Test
void allData(){
List<Excel> excels = excelMapper.selectAllOrderById();
String filePath = "C:\\Users\\ziJian.zheng\\IdeaProjects\\Kira-Test\\src\\main\\resources\\templates\\test.xlsx";
WriteSheet sheet = EasyExcel.writerSheet("批次" ).build();
//用来写入的ExcelWriter
ExcelWriter writer = EasyExcel.write(filePath).build();
writer.write(excels, sheet);
}
常见的注解
@ExcelProperty(value = "用户姓名",index = 0)
效果:index属性可以指定当前字段对应excel中的哪一列,可以根据列名value去匹配,也可以不写
如果不使用@ExcelProperty注解,成员变量从上到下的顺序,对应表格中 从左到右的顺序
@ExcelIgnore
读写的时候会忽略该字段
@DateTimeFormat 日期转换
日期格式转换 :将 Excel 中的日期格式数据(如 2023-01-01)与 Java 字段(String 或 Date)自动转换
@DateTimeFormat("yyyy-MM-dd") // 格式化为"2023-01-01"
private String createDate;
@NumberFormat 数字格式转换
将 Excel 中的数字(如 1,000.50)与 Java 字段(String 或数值类型)自动转换
@NumberFormat("#,##0.00") // 格式化为"1,000.50"
private String price;
@ExcelIgnoreUnannotated
制类字段的默认行为 :标注在类上,决定未加 @ExcelProperty 的字段是否参与读写。
不标注该注解 :所有字段(无论是否有 @ExcelProperty)都会参与读写。
标注该注解 :只有显式标注 @ExcelProperty 的字段参与读写