聊聊Excel解析:如何处理百万行EXCEL文件?( 五 )


(2)SXSSFSXSSF 简介
SXSSF,全称 Streaming XML SpreadSheet Format,是 POI 3.8-beta3 版本后推出的低内存占用的流式 Excel API,旨在解决 Excel 写入时的内存问题 。它是 XSSF 的扩展,当需要将大批量数据写入 Excel 中时,只需要用 SXSSF 替换 XSSF 即可 。SXSSF 的原理是滑动窗口 —— 在内存中保存一定数量的行,其余行存储在磁盘 。这么做的好处是内存优化,代价是失去了随机访问的能力 。SXSSF 可以兼容 XSSF 的绝大多数 API,非常适合了解 UserModel 的开发者 。
内存优化会难以避免地带来一定限制:
① 在某个时间点只能访问有限数量的行,因为其余行并未被加载入内存 。
② 不支持需要随机访问的 XSSF API,如删除 / 移动行、克隆 sheet、公式计算等 。
③ 不支持 Excel 读取操作 。
④ 正因为它是 XSSF 的扩展,所以不支持写入 Xls 文件 。
UserModel、EventModel、SXSSF 对比
到这里就介绍完了所有的 POI Excel API,下表是所有这些 API 的功能对比,来自 POI 官网:

聊聊Excel解析:如何处理百万行EXCEL文件?

文章插图
可以看到,UserModel 基于 DOM 解析,功能是最齐全的,支持随机访问,唯一缺点是 CPU 和内存效率不稳定;
EventModel 是 POI 提供的流式读取方案,基于 SAX 解析,仅支持向前访问,其余 API 不支持;
SXSSF 是 POI 提供的流式写入方案,同样仅能向前访问,支持部分 XSSF API 。
(3)EasyExcelEasyExcel 简介
为了解决 POI 原生的 SAX 解析的问题,阿里基于 POI 二次开发了 EasyExcel 。下面是引用自 EasyExcel 官网的介绍:
Java 解析、生成 Excel 比较有名的框架有 Apache poi、jxl 。但他们都存在一个严重的问题就是非常的耗内存,poi 有一套 SAX 模式的 API 可以一定程度的解决一些内存溢出的问题,但 POI 还是有一些缺陷,比如 07 版 Excel 解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大 。easyexcel 重写了 poi 对 07 版 Excel 的解析,一个 3M 的 excel 用 POI sax 解析依然需要 100M 左右内存,改用 easyexcel 可以降低到几 M,并且再大的 excel 也不会出现内存溢出;03 版依赖 POI 的 sax 模式,在上层做了模型转换的封装,让使用者更加简单方便 。
如介绍所言,EasyExcel 同样采用 SAX 方式解析,但由于重写了 xlsx 的 SAX 解析,优化了内存开销;对 xls 文件,在上层进一步进行了封装,降低了使用成本 。API 上,采用注解的方式去定义 Excel 实体类,使用方便;通过事件监听器的方式做 Excel 读取,相比于原生 EventModel,API 大大简化;写入数据时,EasyExcel 对大批数据,通过重复多次写入的方式从而降低内存开销 。
EasyExcel 最大的优势是使用简便,十分钟可以上手 。由于对 POI 的 API 都做了高级封装,所以适合不想了解 POI 基础 API 的开发者 。总之,EasyExcel 是一款值得一试的 API 。
使用 EasyExcel
引入 easyexcel 依赖:
< dependency>
< groupId> com.alibaba </ groupId>
< artifactId> easyexcel </ artifactId>
< version> 2.2.3 </ version>
</ dependency>
首先,用注解定义 Excel 实体类:
importcom.alibaba.excel. annotation.ExcelProperty;
importlombok.Data;
@Data
publicclassSku{
@ExcelProperty(index = 0)
privateLongid;
@ExcelProperty(index = 1)
privateString name;
@ExcelProperty(index = 2)
privateDoubleprice;
}
接下来,重写 AnalysisEventListener 中的 invoke 和 doAfterAllAnalysed 方法,这两个方法分别在监听到单行解析完成的事件时和全部解析完成的事件时调用 。每次单行解析完成时,我们打印解析结果,代码如下:
importcom.alibaba.excel.EasyExcel;
importcom.alibaba.excel.context.AnalysisContext;
importcom.alibaba.excel.event.AnalysisEventListener;
importcom.alibaba.fastjson.JSON;
importorg.shy.domain.pojo.easyexcel.Sku;
publicclassMyEasyExcel{
publicstaticvoidmain(String[] args){
parseSku;
}
publicstaticvoidparseSku{
//读取文件路径
String fileName = "D:sunhaoyu8DocumentsFilesexcel.xlsx";
//读取excel
EasyExcel.read(fileName, Sku . class, newAnalysisEventListener< Sku> {
@Override
publicvoidinvoke(Sku sku, AnalysisContext analysisContext){
System.out.println( "第"+ analysisContext.getCurrentRowNum + "行:"+ JSON.toJSONString(sku));
}
@Override
publicvoiddoAfterAllAnalysed(AnalysisContext analysisContext){


推荐阅读