Java解析excel工具easyexcel 助你快速简单避免OOM
原文链接:http://www.itsleuth.cn/post/javatool-001-easyexcel/
Java解析、生成Excel比较有名的框架有Apache poi、jxl。但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大。easyexcel重写了poi对07版Excel的解析,能够原本一个3M的excel用POI sax依然需要100M左右内存降低到KB级别,并且再大的excel不会出现内存溢出,03版依赖POI的sax模式。在上层做了模型转换的封装,让使用者更加简单方便
easyexcel核心功能
- 读任意大小的03、07版Excel不会OOM
- 读Excel自动通过注解,把结果映射为java模型
- 读Excel支持多sheet
- 读Excel时候是否对Excel内容做trim()增加容错
- 写小量数据的03版Excel(不要超过2000行)
- 写任意大07版Excel不会OOM
- 写Excel通过注解将表头自动写入Excel
- 写Excel可以自定义Excel样式 如:字体,加粗,表头颜色,数据内容颜色
- 写Excel到多个不同sheet
- 写Excel时一个sheet可以写多个Table
- 写Excel时候自定义是否需要写表头
快速使用
1. JAR包依赖
使用前最好咨询下最新版,或者到mvn仓库搜索一下easyexcel的最新版
<dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>{latestVersion}</version>
</dependency>
2. 读取Excel
使用easyexcel解析03、07版本的Excel只是ExcelTypeEnum不同,其他使用完全相同,使用者无需知道底层解析的差异。
无java模型直接把excel解析的每行结果以List返回 在ExcelListener获取解析结果
读excel代码示例如下:
@Testpublic void testExcel2003NoModel() {InputStream inputStream = getInputStream("loan1.xls");try {// 解析每行结果在listener中处理ExcelListener listener = new ExcelListener();ExcelReader excelReader = new ExcelReader(inputStream, ExcelTypeEnum.XLS, null, listener);excelReader.read();} catch (Exception e) {} finally {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}
ExcelListener示例代码如下:
/* 解析监听器,* 每解析一行会回调invoke()方法。* 整个excel解析结束会执行doAfterAllAnalysed()方法** 下面只是我写的一个样例而已,可以根据自己的逻辑修改该类。* @author jipengfei* @date 2017/03/14*/
public class ExcelListener extends AnalysisEventListener {//自定义用于暂时存储data。//可以通过实例获取该值private List<Object> datas = new ArrayList<Object>();public void invoke(Object object, AnalysisContext context) {System.out.println("当前行:"+context.getCurrentRowNum());System.out.println(object);datas.add(object);//数据存储到list,供批量处理,或后续自己业务逻辑处理。doSomething(object);//根据自己业务做处理}private void doSomething(Object object) {//1、入库调用接口}public void doAfterAllAnalysed(AnalysisContext context) {// datas.clear();//解析结束销毁不用的资源}public List<Object> getDatas() {return datas;}public void setDatas(List<Object> datas) {this.datas = datas;}
}
有java模型映射
java模型写法如下:
public class LoanInfo extends BaseRowModel {@ExcelProperty(index = 0)private String bankLoanId;@ExcelProperty(index = 1)private Long customerId;@ExcelProperty(index = 2,format = "yyyy/MM/dd")private Date loanDate;@ExcelProperty(index = 3)private BigDecimal quota;@ExcelProperty(index = 4)private String bankInterestRate;@ExcelProperty(index = 5)private Integer loanTerm;@ExcelProperty(index = 6,format = "yyyy/MM/dd")private Date loanEndDate;@ExcelProperty(index = 7)private BigDecimal interestPerMonth;@ExcelProperty(value = {"一级表头","二级表头"})private BigDecimal sax;
}
@ExcelProperty(index = 3)数字代表该字段与excel对应列号做映射,也可以采用 @ExcelProperty(value = {“一级表头”,“二级表头”})用于解决不确切知道excel第几列和该字段映射,位置不固定,但表头的内容知道的情况。
@Testpublic void testExcel2003WithReflectModel() {InputStream inputStream = getInputStream("loan1.xls");try {// 解析每行结果在listener中处理AnalysisEventListener listener = new ExcelListener();ExcelReader excelReader = new ExcelReader(inputStream, ExcelTypeEnum.XLS, null, listener);excelReader.read(new Sheet(1, 2, LoanInfo.class));} catch (Exception e) {} finally {try {inputStream.close();} catch (IOException e) {e.printStackTrace();}}}
带模型解析与不带模型解析主要在构造new Sheet(1, 2, LoanInfo.class)时候包含class。Class需要继承BaseRowModel暂时BaseRowModel没有任何内容,后面升级可能会增加一些默认的数据。
3. 生成Excel
每行数据是List无表头
OutputStream out = new FileOutputStream("/Users/jipengfei/77.xlsx");try {ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX,false);//写第一个sheet, sheet1 数据全是List<String> 无模型映射关系Sheet sheet1 = new Sheet(1, 0);sheet1.setSheetName("第一个sheet");writer.write(getListString(), sheet1);writer.finish();} catch (Exception e) {e.printStackTrace();} finally {try {out.close();} catch (IOException e) {e.printStackTrace();}}
每行数据是一个java模型有表头----表头层级为一
生成Excel格式如下图:
模型写法如下:
public class ExcelPropertyIndexModel extends BaseRowModel {@ExcelProperty(value = "姓名" ,index = 0)private String name;@ExcelProperty(value = "年龄",index = 1)private String age;@ExcelProperty(value = "邮箱",index = 2)private String email;@ExcelProperty(value = "地址",index = 3)private String address;@ExcelProperty(value = "性别",index = 4)private String sax;@ExcelProperty(value = "高度",index = 5)private String heigh;@ExcelProperty(value = "备注",index = 6)private String last;
}
@ExcelProperty(value = “姓名”,index = 0) value是表头数据,默认会写在excel的表头位置,index代表第几列。
@Testpublic void test1() throws FileNotFoundException {OutputStream out = new FileOutputStream("/Users/jipengfei/78.xlsx");try {ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX);//写第一个sheet, sheet1 数据全是List<String> 无模型映射关系Sheet sheet1 = new Sheet(1, 0,ExcelPropertyIndexModel.class);writer.write(getData(), sheet1);writer.finish();} catch (Exception e) {e.printStackTrace();} finally {try {out.close();} catch (IOException e) {e.printStackTrace();}}}
每行数据是一个java模型有表头----表头层级为多层级
生成Excel格式如下图:
java模型写法如下:
public class MultiLineHeadExcelModel extends BaseRowModel {@ExcelProperty(value = {"表头1","表头1","表头31"},index = 0)private String p1;@ExcelProperty(value = {"表头1","表头1","表头32"},index = 1)private String p2;@ExcelProperty(value = {"表头3","表头3","表头3"},index = 2)private int p3;@ExcelProperty(value = {"表头4","表头4","表头4"},index = 3)private long p4;@ExcelProperty(value = {"表头5","表头51","表头52"},index = 4)private String p5;@ExcelProperty(value = {"表头6","表头61","表头611"},index = 5)private String p6;@ExcelProperty(value = {"表头6","表头61","表头612"},index = 6)private String p7;@ExcelProperty(value = {"表头6","表头62","表头621"},index = 7)private String p8;@ExcelProperty(value = {"表头6","表头62","表头622"},index = 8)private String p9;
}
写Excel写法同上,只需将ExcelPropertyIndexModel.class改为MultiLineHeadExcelModel.class
一个Excel多个sheet写法
@Testpublic void test1() throws FileNotFoundException {OutputStream out = new FileOutputStream("/Users/jipengfei/77.xlsx");try {ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX,false);//写第一个sheet, sheet1 数据全是List<String> 无模型映射关系Sheet sheet1 = new Sheet(1, 0);sheet1.setSheetName("第一个sheet");writer.write(getListString(), sheet1);//写第二个sheet sheet2 模型上打有表头的注解,合并单元格Sheet sheet2 = new Sheet(2, 3, MultiLineHeadExcelModel.class, "第二个sheet", null);sheet2.setTableStyle(getTableStyle1());writer.write(getModeldatas(), sheet2);//写sheet3 模型上没有注解,表头数据动态传入List<List<String>> head = new ArrayList<List<String>>();List<String> headCoulumn1 = new ArrayList<String>();List<String> headCoulumn2 = new ArrayList<String>();List<String> headCoulumn3 = new ArrayList<String>();headCoulumn1.add("第一列");headCoulumn2.add("第二列");headCoulumn3.add("第三列");head.add(headCoulumn1);head.add(headCoulumn2);head.add(headCoulumn3);Sheet sheet3 = new Sheet(3, 1, NoAnnModel.class, "第三个sheet", head);writer.write(getNoAnnModels(), sheet3);writer.finish();} catch (Exception e) {e.printStackTrace();} finally {try {out.close();} catch (IOException e) {e.printStackTrace();}}}
一个sheet中有多个表格
@Testpublic void test2() throws FileNotFoundException {OutputStream out = new FileOutputStream("/Users/jipengfei/77.xlsx");try {ExcelWriter writer = new ExcelWriter(out, ExcelTypeEnum.XLSX,false);//写sheet1 数据全是List<String> 无模型映射关系Sheet sheet1 = new Sheet(1, 0);sheet1.setSheetName("第一个sheet");Table table1 = new Table(1);writer.write(getListString(), sheet1, table1);writer.write(getListString(), sheet1, table1);//写sheet2 模型上打有表头的注解Table table2 = new Table(2);table2.setTableStyle(getTableStyle1());table2.setClazz(MultiLineHeadExcelModel.class);writer.write(getModeldatas(), sheet1, table2);//写sheet3 模型上没有注解,表头数据动态传入,此情况下模型field顺序与excel现实顺序一致List<List<String>> head = new ArrayList<List<String>>();List<String> headCoulumn1 = new ArrayList<String>();List<String> headCoulumn2 = new ArrayList<String>();List<String> headCoulumn3 = new ArrayList<String>();headCoulumn1.add("第一列");headCoulumn2.add("第二列");headCoulumn3.add("第三列");head.add(headCoulumn1);head.add(headCoulumn2);head.add(headCoulumn3);Table table3 = new Table(3);table3.setHead(head);table3.setClazz(NoAnnModel.class);table3.setTableStyle(getTableStyle2());writer.write(getNoAnnModels(), sheet1, table3);writer.write(getNoAnnModels(), sheet1, table3);writer.finish();} catch (Exception e) {e.printStackTrace();} finally {try {out.close();} catch (IOException e) {e.printStackTrace();}}}
4. 测试数据分析
从上面的性能测试可以看出easyexcel在解析耗时上比poiuserModel模式弱了一些。主要原因是我内部采用了反射做模型字段映射,中间我也加了cache,但感觉这点差距可以接受的。但在内存消耗上差别就比较明显了,easyexcel在后面文件再增大,内存消耗几乎不会增加了。但poi userModel就不一样了,简直就要爆掉了。想想一个excel解析200M,同时有20个人再用估计一台机器就挂了。
5. 百万数据解析对比
easyexcel解析百万数据内存图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YVo5Q2gh-1631244928914)(https://www.itsleuth.cn/upload/2018/7/201807101724572018071110170464.png “easyexcel解析百万数据内存图”)]
poi解析百万数据内存图如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9Ebaubd6-1631244928915)(https://www.itsleuth.cn/upload/2018/7/201807101732472018071110170462.png “poi解析百万数据内存图”)]
从上面两图可以看出,easyexcel解析时内存消耗很少,最多消耗不到50M;POI解析过程中直接飘升到1.5G左右,系统内存耗尽,程序挂掉。
GitHub地址:https://github.com/alibaba/easyexcel
Java解析excel工具easyexcel 助你快速简单避免OOM相关推荐
- java解析excel工具EasyExcel使用详情
EasyExcel 1 EasyExcel的集成 1.1 引入依赖 1.2 模型映射 1.3 读Excel 1.4 写Excel 1.5 web上传.下载 2 自定义多Sheet页下载 2.1 工具类 ...
- easyexcel生成excel_阿里JAVA解析Excel工具easyexcel
java解析.生成Excel比较有名的框架有Apache poi.jxl.但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内存溢出的问题,但POI还是有 ...
- JAVA解析Excel工具EasyExcel
我是傲骄鹿先生,沉淀.学习.分享.成长. 如果你觉得文章内容还可以的话,希望不吝您的「一键三连」,文章里面有不足的地方希望各位在评论区补充疑惑.见解以及面试中遇到的奇葩问法 目录 64M内存20秒读取 ...
- java解析excel的工具_Java 解析 Excel 工具 easyexcel
软件介绍 easyexcel -- JAVA 解析 Excel 工具 Java 解析.生成 Excel 比较有名的框架有 Apache poi.jxl .但他们都存在一个严重的问题就是非常的耗内存,p ...
- 使用EasyExcel导入导出Excel报表-JAVA解析Excel工具
一.EasyExcel概述 Java解析.生成Excel比较有名的框架有Apache poi.jxl.但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一定程度的解决一些内 ...
- java sax 解析excel,使用EasyExcel导入导出Excel报表-JAVA解析Excel工具
1.EasyExcel概述 Java解析.生成Excel比较有名的框架有Apache poi.jxl.但他们都存在一个严重的问题就是很是的耗内存,poi有一套SAX模式的API能够必定程度的解决一些内 ...
- Java解析Excel工具类(兼容xls和xlsx)
依赖jar <dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml< ...
- Java操作excel工具easyExcel
推荐阅读: https://blog.csdn.net/jiangjiandecsd/article/details/81115622 转载于:https://www.cnblogs.com/mxh- ...
- Java写Excel时OOM_JAVA解析Excel工具避免OOM
JAVA解析Excel工具easyexcel Java解析.生成Excel比较有名的框架有Apache poi.jxl.但他们都存在一个严重的问题就是非常的耗内存,poi有一套SAX模式的API可以一 ...
最新文章
- c 获取mysql列数据_转 用C API 操作MySQL数据库
- AutoRunner视图模式的合理使用
- golang 面向对象编程
- java web基础1Tomcat服务器基本知识
- c# 中文字符(全角、半角)通用处理
- hdu6103[尺取法] 2017多校6
- java excil表格开发_JAVA导出Excel电子表格的方法
- “非IE内核浏览器”第一阶段开发计划发布
- windows server 2003双网卡的问题
- ftp 服务器创建访问连接抱错_如何用固定IP连接FTP服务器?
- VDI成长之路:Windows桌面优化配置(持续更新-20120227)
- R语言如何做NMDS分析
- 微型计算机的主体,以微型计算机为主体,配上系统软件和外设就组成了微型计算机系统...
- vs code git 编辑器中拉取(pull) 的时候报错 [rejected] v1.0.0 -> v1.0.0 (would clobber existing tag)
- boost入门(一):boost简介
- Redis基础知识笔记
- MATLAB编写用户登陆界面小结——更改界面左上角图标、输入用户名提醒和输入密码隐藏
- 信息武器化——认知安全的必要性
- 用d2rq转换MySQL为RDF数据
- Android setEnabled() 和 setClickable() 的区别