我去,还在这样读写 excel 这也太低效了吧!
前言
最近读者小 h 给小黑哥发来私信:
小黑哥,最近我在负责公司报表平台开发,需要导出报表到 excel 中。每次使用 poi 开发,都要写长长的一坨代码,好几次因为没加入判空判断,导致生成失败。想跟你请教下有没有更加高效一点读写 excel 方法?
使用过 poi 的开发同学可能都有此体会,每次都要写一坨代码,最后的代码如下面一样:
这样的代码是不是又臭又长?当字段数量多的时候,一不小心还容易写错。小黑哥还记得当初使用 poi 导出一个二十多字段的 excel,不断复制粘贴,行号一不小心就写错了,那叫个一个心酸。
今天小黑哥就来推荐一个阿里开源的项目『easyexcel』,带大家彻底告别上面又长又臭的代码,彻底解决这个问题。
easyexcel
easyexcel 是一个阿里出品的开源项目 ,看名字就能看出这个项目是为了让你更加简单的操作 excel。另外 easyexcel 还解决了poi 内存溢出问题,修复了一些并发情况下一些 bug。
github 地址:
截止小黑哥写文章时,已有 13.6k star 数据,可见这个项目还是深受大家欢迎。
废话不多说,我们直接进入源码实战环节。
首先我们需要引入 easyexcel pom 依赖:
<dependency> <groupid>com.alibaba</groupid> <artifactid>easyexcel</artifactid> <version>2.1.6</version> </dependency>
这里建议大家使用 2.0 以上的正式版本,不要再使用 1.0 的老版本,两者使用 api 差别很大。另外 beta 版本可能会存在某些 bug,大家谨慎使用。
普通方式
一行代码生成 excel
// 写法1 string filename = "temp/" + "test" + system.currenttimemillis() + ".xlsx"; easyexcel.write(filename) .head(head())// 设置表头 .sheet("模板")// 设置 sheet 的名字 // 自适应列宽 .registerwritehandler(new longestmatchcolumnwidthstylestrategy()) .dowrite(datalist());// 写入数据
生成 excel 代码特别简单,这里使用链式语句,一行代码直接搞定生成代码。代码中再也不用我们指定行号,列号了。
上面代码中使用自适应列宽的策略。
下面我们来看下表头与标题如何生成。
创建表头
/** * 创建表头,可以创建复杂的表头 * * @return */ private static list<list<string>> head() { list<list<string>> list = new arraylist<list<string>>(); // 第一列表头 list<string> head0 = new arraylist<string>(); head0.add("第一列"); head0.add("第一列第二行"); // 第二列表头 list<string> head1 = new arraylist<string>(); head1.add("第一列"); head1.add("第二列第二行"); // 第三列 list<string> head2 = new arraylist<string>(); head2.add("第一列"); head2.add("第三列第二行"); list.add(head0); list.add(head1); list.add(head2); return list; }
上面每个 list<string>
代表一列的数据,集合内每个数据将会顺序写入这列每一行。如果每一列的相同行数的内容相同,将会自动合并单元格。通过这个规则,我们创建复杂的表头。
最终创建表头如下:
写入表体数据
private static list datalist() { list<list<object>> list = new arraylist<list<object>>(); for (int i = 0; i < 10; i++) { list<object> data = new arraylist<object>(); data.add("点赞+" + i); // date 将会安装 yyyy-mm-dd hh:mm:ss 格式化 data.add(new date()); data.add(0.56); list.add(data); } return list; }
表体数据然后也是使用 list<list<object>>
,但是与表头规则不一样。
每个 list<object>
代表一行的数据,数据将会按照顺序写入每一列中。
集合中数据 easyexcel 将会按照默认的格式化转换输出,比如 date
类型数据就将会按照 yyyy-mm-dd hh:mm:ss
格式化。
如果需要转化成其他格式,建议直接将数据格式化成字符串加入 list,不要通过 easyexcel 转换。
最终效果如下:
看完这个是不是想立刻体验一下?等等,上面使用方式还是有点繁琐,使用 easyexcel 还可以更快。我们可以使用注解方式,无需手动设置表头与表体。
注解方式
注解方式生成 excel 代码如下:
string filename = "temp/annotatewrite" + system.currenttimemillis() + ".xlsx"; // 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭 // 如果这里想使用03 则 传入exceltype参数即可 easyexcel .write(filename, demodata.class) .sheet("注解方式") .registerwritehandler(createtablestyle())// excel 表格样式 .dowrite(data());
这里代码与上面大体一致,只不过这里需要在 write
方法传入 demodata
数据类型。easyexcel 会根据 demodata
类型自动生成表头。
下面我们来看下 demodata
这个类到底内部到底是啥样?
@contentrowheight(30)// 表体行高 @headrowheight(20)// 表头行高 @columnwidth(35)// 列宽 @data public class demodata { /** * 单独设置该列宽度 */ @columnwidth(50) @excelproperty("字符串标题") private string string; /** * 年月日时分秒格式 */ @datetimeformat("yyyy年mm月dd日hh时mm分ss秒") @excelproperty(value = "日期标题") private date date; /** * 格式化百分比 */ @numberformat("#.##%") @excelproperty("数字标题") private double doubledata; @excelproperty(value = "枚举类",converter = demoenumconvert.class) private demoenum demoenum; /** * 忽略这个字段 */ @excelignore private string ignore; }
demodata
就是一个普通的 pojo
类,上面使用 exayexcel 相关注解,exayexcel 将会通过反射读取字段类型以及相关注解,然后直接生成 excel 。
exayexcel 提供相关注解类,直接定义 excel 的数据模型:
-
@excelproperty
指定当前字段对应excel中的那一列,内部 value 属性指定表头列的名称 -
@excelignore
默认所有字段都会和excel去匹配,加了这个注解会忽略该字段 -
@contentrowheight
指定表体行高 -
@headrowheight
指定表头行高 -
@columnwidth
指定列的宽度
另外 exayexcel 还提供几个注解,自定义日期以及数字的格式化转化。
@datetimeformat
@numberformat
另外我们可以自定义格式化转换方案,需要实现 converter
类相关方法即可。
public class demoenumconvert implements converter<demoenum> { @override public class supportjavatypekey() { return demoenum.class; } @override public celldatatypeenum supportexceltypekey() { return celldatatypeenum.string; } /** * excel 转化为 java 类型,excel 读时将会被调用 * @param celldata * @param contentproperty * @param globalconfiguration * @return * @throws exception */ @override public demoenum converttojavadata(celldata celldata, excelcontentproperty contentproperty, globalconfiguration globalconfiguration) throws exception { return null; } /** * java 类型转 excel 类型,excel 写时将会被调用 * @param value * @param contentproperty * @param globalconfiguration * @return * @throws exception */ @override public celldata converttoexceldata(demoenum value, excelcontentproperty contentproperty, globalconfiguration globalconfiguration) throws exception { return new celldata(value.getdesc()); } }
最后我们还需要在 @excelproperty
注解上使用 converter
指定自定义格式转换方案。
使用方式如下:
@excelproperty(value = "枚举类",converter = demoenumconvert.class) private demoenum demoenum;
最后我们运行一下,看下 excel 实际效果如何:
怎么样,效果还是可以吧。
对了,默认的样式表格样式可不是这样,这个效果是因为我们在 registerwritehandler
方法中设置自定义的样式,具体代码如下:
/*** * 设置 excel 的样式 * @return */ private static writehandler createtablestyle() { // 头的策略 writecellstyle headwritecellstyle = new writecellstyle(); // 背景设置为红色 headwritecellstyle.setfillforegroundcolor(indexedcolors.pink.getindex()); // 设置字体 writefont headwritefont = new writefont(); headwritefont.setfontheightinpoints((short) 20); headwritecellstyle.setwritefont(headwritefont); // 内容的策略 writecellstyle contentwritecellstyle = new writecellstyle(); // 这里需要指定 fillpatterntype 为fillpatterntype.solid_foreground 不然无法显示背景颜色.头默认了 fillpatterntype所以可以不指定 contentwritecellstyle.setfillpatterntype(fillpatterntype.solid_foreground); // 背景绿色 contentwritecellstyle.setfillforegroundcolor(indexedcolors.lemon_chiffon.getindex()); writefont contentwritefont = new writefont(); // 字体大小 contentwritefont.setfontheightinpoints((short) 20); contentwritecellstyle.setwritefont(contentwritefont); // 设置边框的样式 contentwritecellstyle.setborderbottom(borderstyle.dashed); contentwritecellstyle.setborderleft(borderstyle.dashed); contentwritecellstyle.setborderright(borderstyle.dashed); contentwritecellstyle.setbordertop(borderstyle.dashed); // 这个策略是 头是头的样式 内容是内容的样式 其他的策略可以自己实现 horizontalcellstylestrategy horizontalcellstylestrategy = new horizontalcellstylestrategy(headwritecellstyle, contentwritecellstyle); return horizontalcellstylestrategy; }
使用注意点
poi 冲突问题
理论上当前 easyexcel
兼容支持 poi 的3.17
,4.0.1
,4.1.0
所有较新版本,但是如果项目之前使用较老版本的 poi,由于 poi 内部代码调整,某些类已被删除,这样直接运行时很大可能会抛出以下异常:
nosuchmethodexception
classnotfoundexception
noclassdeffounderror
所以使用过程中一定要注意统一项目中的 poi 的版本。
非注解方式自定义行高列宽
非注解方式自定义行高以及列宽比较麻烦,暂时没有找到直接设置的入口。查了一遍 github 相关 issue,开发人员回复需要实现 writehandler
接口,自定义表格样式。
总结
本文主要给各位小伙伴们安利 easyexcel 强大的功能,介绍 easyexcel 两种生成 excel 方式,以及演示相关的示例代码。 easyexcel 除了写之外,当然还支持快读读取 excel 的功能,这里就不再详细介绍。github 上相关文档例子非常丰富,大家可以自行参考。
github 文档地址:
reference
欢迎关注我的公众号:程序通事,获得日常干货推送。如果您对我的专题内容感兴趣,也可以关注我的博客:
上一篇: 天猫国际618:小众网红旅游国家商品首日销售同比呈3位数增长
下一篇: 移动端判断手机横竖屏状态