System.out. println( JSON.toJSONString(skus));
}
publicstaticList< Sku> parseSkus( StringfilePath) throwsException{
FileInputStreamin= new FileInputStream(filePath);
Workbookwk = new XSSFWorkbook( in);
Sheetsheet = wk.getSheetAt( 0);
// 转换成的数据列表
List< Sku> skus = new ArrayList<>;
// 获取Sku的注解信息
Map< Integer, Field> fieldMap = new HashMap<>( 16);
for( Fieldfield : Sku. class. getDeclaredFields) {
ExcelColcol = field.getAnnotation( ExcelCol. class);
if( col== null) {
continue;
}
field.setAccessible( true);
fieldMap.put(col.index, field);
}
for(int rowNum = 1; rowNum <= sheet.getLastRowNum; rowNum++) {
Rowr = sheet.getRow(rowNum);
Skusku = new Sku;
for(int cellNum = 0; cellNum < fieldMap.size; cellNum++) {
Cellc= r.getCell(cellNum);
if( c!= null) {
setFieldValue(fieldMap. get(cellNum), getCellValue( c), sku);
}
}
skus.add(sku);
}
returnskus;
}
publicstaticvoid setFieldValue( Fieldfield, Stringvalue, Skusku) throwsException{
if(field == null) {
return;
}
//得到此属性的类型
Stringtype = field.getType. toString;
if( StringUtils.isBlank(value)) {
field. set(sku, null);
} elseif(type.endsWith( "String")) {
field. set(sku, value);
} elseif(type.endsWith( "long") || type.endsWith( "Long")) {
field. set(sku, Long.parseLong(value));
} elseif(type.endsWith( "double") || type.endsWith( "Double")) {
field. set(sku, Double.parseDouble(value));
} else{
field. set(sku, value);
}
}
publicstaticStringgetCellValue( Cellcell) {
DecimalFormatdf = new DecimalFormat( "#.##");
if(cell == null) {
return"";
}
switch(cell.getCellType) {
caseNUMERIC:
returndf.format(cell.getNumericCellValue);
caseSTRING:
returncell.getStringCellValue.trim;
caseBLANK:
returnnull;
}
return"";
}
最后,将转换完成的数据列表打印出来 。运行结果如下:
[{ "id": 345000, "name": "电脑A", "price": 5999.0},{ "id": 345001, "name": "手机C", "price": 4599.0}]
Tips:如果您的程序出现 “NoClassDefFoundError”,请引入 ooxml-schemas 依赖:
< dependency>
< groupId> org.apache.poi </ groupId>
< artifactId> ooxml-schemas </ artifactId>
< version> 1.4 </ version>
</ dependency>
版本选择见下表,如 POI 4.0.0 对应 ooxml-schemas 1.4 版本:
UserModel 的局限
以上处理逻辑对于大部分的 Excel 文件都很适用,但最大的缺点是内存开销大,因为所有的数据都被加载入内存 。实测,以上 3 列的 Excel 文件在 7 万行左右就会出现 OOM,而 XLS 文件最大行数为 65535 行,XLSX 更是达到了 1048576 行,如果将几万甚至百万级别的数据全部读入内存,内存溢出风险极高 。
那么,该如何解决传统 UserModel 无法处理大批量 Excel 的问题呢?开发者们给出了许多精彩的解决方案,请看下一章 。
三、进阶篇 - 内存优化的探索
接下来介绍本文重点内容,同时解决本文所提出的问题: 如何进行 Excel 解析的内存优化,从而处理百万行 Excel 文件?
(1)EventModel
前面我们提到,除了 UserModel 外,POI 还提供了另一种解析 Excel 的模型:EventModel 事件模型 。不同于用户模型的 DOM 解析,事件模型采用了 SAX 的方式去解析 Excel 。
EventModel & SAX 解析
SAX 的全称是 Simple API for XML,是一种基于事件驱动的 XML 解析方法 。不同于 DOM 一次性读入 XML,SAX 会采用边读取边处理的方式进行 XML 操作 。简单来讲,SAX 解析器会逐行地去扫描 XML 文档,当遇到标签时会触发解析处理器,从而触发相应的事件 Handler 。我们要做的就是继承 DefaultHandler 类,重写一系列事件处理方法,即可对 Excel 文件进行相应的处理 。
下面是一个简单的 SAX 解析的示例,这是要解析的 XML 文件:一个 sku 表,其中包含两个 sku 节点,每个节点有一个 id 属性和三个子节点 。
<?xml version="1.0" encoding="UTF-8"?>
推荐阅读
- 深入解析Redis的LRU与LFU算法实现
- 我们一起聊聊数据库与容器
- 逆水寒手游究竟好不好玩,逆水寒手游深度解析
- excel打印区域设置虚线怎样显示出来
- excel表格中如何换行打字内容
- excel表格里的斜线怎么画出来
- excel表格的斜划线怎么做的
- excel表格中画斜线表头
- excel表格画斜线写字
- excel 自动生成目录超链接
