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


使用十万行量级的 excel 文件实测一下,运行结果:

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

文章插图
StAX 解析
Xlsx-streamer 底层采用的解析方式,被称作 StAX 解析 。StAX 于 2004 年 3 月在 JSR 173 规范中引入,是 JDK 6.0 推出的新特性 。它的全称是 Streaming API for XML,流式 XML 解析 。更准确地讲,称作 “流式拉分析” 。之所以称作拉分析,是因为它和 “流式推分析”——SAX 解析相对 。
之前我们提到,SAX 解析是一种事件驱动的解析模型,每当解析到标签时都会触发相应的事件 Handler,将事件 “推” 给响应器 。在这样的推模型中,解析器是主动,响应器是被动,我们不能选择想要响应哪些事件,因此这样的解析比较不灵活 。
为了解决 SAX 解析的问题,StAX 解析采用了 “拉” 的方式 —— 由解析器遍历流时,原来的响应器变成了驱动者,主动遍历事件解析器(迭代器),从中拉取一个个事件并处理 。在解析过程中,StAX 支持使用 peek 方法来 "偷看" 下一个事件,从而决定是否有必要分析下一个事件,而不必从流中读取事件 。这样可以有效提高灵活性和效率 。
下面用 StAX 的方式再解析一下相同的 XML:
<?xml version="1.0" encoding="UTF-8"?>
< skus>
< skuid= "345000">
< name> 电脑A </ name>
< price> 5999.0 </ price>
</ sku>
< skuid= "345001">
< name> 手机C </ name>
< price> 4599.0 </ price>
</ sku>
</ skus>
这次我们不需要监听器,把所有处理的逻辑集成在一个方法中:
importcom.alibaba.fastjson.JSON;
importorg.apache.commons.lang3.StringUtils;
importorg.shy.domain.pojo.Sku;
importjavax.xml.stream.XMLEventReader;
importjavax.xml.stream.XMLInputFactory;
importjavax.xml.stream.events.Attribute;
importjavax.xml.stream.events.StartElement;
importjavax.xml.stream.events.XMLEvent;
importjava.io.InputStream;
importjava.util.Iterator;
publicclassMyStax{
/**
* 当前正在处理的sku
*/
privatestaticSkusku;
/**
* 当前正在处理的节点名称
*/
privatestaticStringtagName;
publicstaticvoid main( String[] args) throwsException{
parseSku;
}
publicstaticvoid parseSku throwsException{
XMLInputFactoryinputFactory = XMLInputFactory.newInstance;
InputStreaminputStream = ClassLoader.getSystemResourceAsStream( "skus.xml");
XMLEventReaderxmlEventReader = inputFactory.createXMLEventReader(inputStream);
while(xmlEventReader.hasNext) {
XMLEventevent = xmlEventReader.nextEvent;
// 开始节点
if(event.isStartElement) {
StartElementstartElement = event.asStartElement;
Stringname = startElement.getName. toString;
if( "sku".equals(name)) {
sku = new Sku;
Iteratoriterator = startElement.getAttributes;
while(iterator.hasNext) {
Attributeattribute = ( Attribute) iterator.next;
if( "id".equals(attribute.getName. toString)) {
sku.setId( Long.valueOf(attribute.getValue));
}
}
}
tagName = name;
}
// 字符
if(event.isCharacters) {
Stringdata = https://www.isolves.com/it/rj/jy/2023-07-06/event.asCharacters.getData.trim;
if( StringUtils.isNotEmpty(data)) {
if( "name".equals(tagName)) {
sku.setName(data);
}
if( "price".equals(tagName)) {
sku.setPrice( Double.valueOf(data));
}
}
}
// 结束节点
if(event.isEndElement) {
Stringname = event.asEndElement.getName. toString;
if( "sku".equals(name)) {
System.out. println( JSON.toJSONString(sku));
// 处理业务逻辑
// ...
}
}
}
}
}
以上代码与 SAX 解析的逻辑是等价的,用 XMLEventReader 作为迭代器从流中读取事件,循环遍历事件迭代器,再根据事件类型做分类处理 。有兴趣的小伙伴可以自己动手尝试一下,探索更多 StAX 解析的细节 。
四、结论
EventModel、SXSSF、EasyExcel 和 Xlsx-streamer 分别针对 UserModel 的内存占用问题给出了各自的解决方案,下面是对所有本文提到的 Excel API 的对比:
 UserModelEventModelSXSSFEasyExcelXlsx-streamer内存占用量高较低低低低全表随机访问是否否否否读 Excel是是否是是读取方式DOMSAX--SAXStAX写 Excel是是是是否 建议您根据自己的使用场景选择适合的 API:


推荐阅读