点击上方“蓝色字体”,选择“设为星标”
第一时间关注最新文章!
✨一起快乐的学习、成长、热爱生活✨
关于导出 Excel 文件,可以说是大多数服务中都需要集成的功能。那么,要如何快速地(偷懒地)去实现这个功能呢?
你可能第一想法是:这还不简单?用 Apache 开源框架 poi, 或者 jxl 都可以实现啊。面向百度编程,把代码模板 copy 下来,根据自己的业务再改改,能有多难?
嗯.. 的确不难,但是你的代码可能是下面这个熊样子的:
上面这段代码看上去是不是又臭又长呢?今天,将教您如何使用 3 行代码搞定 Excel 文件生成功能!
在说如何实现之前,我们先来讨论一下传统 Excel 框架的不足!除了上面说的,Apache poi、jxl 都存在生成 excel 文件不够简单优雅快速外,它们都还存在一个严重的问题,那就是非常耗内存,严重时会导致内存溢出。
POI 虽然目前来说,是 excel 解析框架中被使用最广泛的,但这个框架并不完美。
为什么这么说呢?
开发者们大部分使用 POI,都是使用其 userModel 模式。而 userModel 的好处是上手容易使用简单,随便拷贝个代码跑一下,剩下就是写业务转换了,虽然转换也要写上百行代码,但是还是可控的。
然而 userModel 模式最大的问题是在于,对内存消耗非常大,一个几兆的文件解析甚至要用掉上百兆的内存。现实情况是,很多应用现在都在采用这种模式,之所以还正常在跑是因为并发不大,并发上来后,一定会OOM或者频繁的 full gc。
什么是 EasyExcel? 见名知意,就是让你轻松的操作 Excel 。先来看下 EasyExcel 官方截图:
官方对其的简介是:
快速、简单避免OOM的java处理Excel工具!
主要来说,有以下几点:
传统 Excel 框架,如 Apache poi、jxl 都存在内存溢出的问题;
传统 excel 开源框架使用复杂、繁琐;
EasyExcel 底层还是使用了 poi, 但是做了很多优化,如修复了并发情况下的一些 bug, 具体修复细节,可阅读官方文档https://github.com/alibaba/easyexcel;
com.alibaba
easyexcel
1.1.2-beta5
/**
* 最简单的写
*
1. 创建excel对应的实体对象 参照{@link DemoData}
*
2. 直接写即可
*/
@Test
public void simpleWrite() {
// 写法1
String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, DemoData.class).sheet("模板").doWrite(data());
}
上面这段示例代码中,有一个点很重要:
①:DemoData这个对象就是要写入 Excel 的数据模型对象,等等,你这好像不行吧?表头 head,以及每个单元格内的数据顺序都没指定,能达到想要的效果么?别急,后面会讨论这块!
回过头来,我们来看看 DemoData
这个对象内部到底有什么幺蛾子!
@Data
public class DemoData {
@ExcelProperty("字符串标题")
private String string;
@ExcelProperty("日期标题")
private Date date;
@ExcelProperty("数字标题")
private Double doubleData;
/**
* 忽略这个字段
*/
@ExcelIgnore
private String ignore;
}
ExayExcel 提供注解的方式, 来方便的定义 Excel 需要的数据模型:
①:通过 @ExcelProperty
注解来指定每个字段的列名称,以及下标位置;
同时,上面定义的 data()
方法也很简单,通过循环,创建一个写入模型的 List 集合:
private List data() {Listlist = new ArrayList();for (int i = 0; i < 10; i++) {
DemoData data = new DemoData();
data.setString("字符串" + i);
data.setDate(new Date());
data.setDoubleData(0.56);list.add(data);
}return list;
}
废话不多说,这个快速接入的案例也介绍的差不多了,跑一跑单元测试看下实际效果:
怎么样,效果还是挺棒棒的!
读excel是通过继承AnalysisEventListener监听器并重写invoke方法来实现的
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
public class DemoDataListener extends AnalysisEventListener {
private static final Logger LOGGER = LoggerFactory.getLogger(DemoDataListener.class);
/**
* 每隔5条存储数据库,实际使用中可以3000条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 5;
Listlist = new ArrayList();/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/private DemoDAO demoDAO;public DemoDataListener() {// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/public DemoDataListener(DemoDAO demoDAO) {this.demoDAO = demoDAO;
}/**
* 这个每一条数据解析都会来调用
*
* @param data
* one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Overridepublic void invoke(DemoData data, AnalysisContext context) {
LOGGER.info("解析到一条数据:{}", JSON.toJSONString(data));list.add(data);// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOMif (list.size() >= BATCH_COUNT) {
saveData();// 存储完成清理 listlist.clear();
}
}/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Overridepublic void doAfterAllAnalysed(AnalysisContext context) {// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
LOGGER.info("所有数据解析完成!");
}/**
* 加上存储数据库
*/private void saveData() {
LOGGER.info("{}条数据,开始存储数据库!", list.size());
demoDAO.save(list);
LOGGER.info("存储数据库成功!");
}
}
通过EasyExcel.read读取文件内容
/**
* 最简单的读
*
1. 创建excel对应的实体对象 参照{@link DemoData}
*
2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoDataListener}
*
3. 直接读即可
*/
@Test
public void simpleRead() {
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
// 写法1:
String fileName = TestFileUtil.getPath() + "demo" + File.separator + "demo.xlsx";
// 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
EasyExcel.read(fileName, DemoData.class, new DemoDataListener()).sheet().doRead();
}
后面有时间继续写一下如何动态生成Excel内容,自定义表头以及内容样式,以及合并单元格
六、需要注意的点
比如说,我们需要从数据库中查询出数据量较大时,我们需要在业务层做分片处理,也就是,我们需要分多次查询,再写入,防止内存溢出 OOM.
Excel 03, 07 版本均有行数、列数的限制:
版本 | 最大行 | 最大列 |
---|---|---|
Excel 2003 | 65536 | 256 |
Excel 2007 | 1048576 | 16384 |
csv 由于是文本文件,实际上没有最大行数的限制,但是用 Excel 客户端打开还是多了不显示。
也就是说,如果你想写入更多的行数是不行的,强行这么做,程序会报类似如下异常
Invalid row number (1048576) outside allowable range (0..1048575)
如何解决呢?
分多个 Excel 文件写入;
同一个 Excel 文件,分多个 Sheet 写入;
今天主要给小伙伴介绍了 EasyExcel, 为什么要使用它,以及演示了相关示例代码。当然了,EasyExcel 除了写 Excel 文件外,它还有快速读取 Excel 的功能,由于本文主要介绍的是:如何快色地实现 Excel 文件生成,所以就没有介绍了,有兴趣的小伙伴们,也可以去 GitHub 官网去去查看相关文档。
最后,祝您看完本文后有所收获,下期见!
近期热文:
Follow Me
不言而喻i
希望这篇文章可以帮到你~
欢迎大家点个在看,分享至朋友圈
你我都成为发光之人,“在看+点赞+分享”~(疯狂暗示)