在POI的使用过程中,对大多数API User来说经常面临两个问题,这也是 GridExcel 致力解决的问题。
将Excel处理逻辑抽取出来,封装成工具类。
与大多数Java API一样,POI把更多的精力放在高级功能的处理上,比如Formula(公式)、Conditional Formatting(条件格式)、Zoom(缩放)等。对于仅仅做数据导入导出功能的API User,很少使用这些高级特性,这允许API用户对POI的使用进行简单的封装。
无论是读是写,我们都需要解决Excel中的Columns(列)与Java数据对象Fields(字段)的映射关系,将这种映射关系作为参数(Map对象HashMap或LinkedHashMap),传递给工具类。
对于Columns不难理解,它可以是有序的数字或字母,也可以是其它字符串用来作为首行,表示该列数据的含义。
对于Fields,它的处理需要兼容复杂情况,如下:
value == true?完成:失败;
首先想到,也是大多数封装者都在使用的方式是就是 Reflection API ,从上文 函数编程 章节我们了解到,反射重量级,会降低代码的性能,同时对复杂情况的处理支持性不够好。
这种方式可以更好的支持复杂情况,但是反射依然会降低性能,同时注解对数据对象会造成代码侵入,而且对该工具类封装者的其他使用者无疑会增加学习成本。
这种方式也可以很好的支持复杂情况,但是使用匿名内部类的语法显然患有“垂直问题”(这意味着代码需要太多的线条来表达基本概念),太过冗杂。
这种方式是基于第5条方法调用的字节码指令 invokeDynamic 实现的,直接传递函数代码块,很好的支持复杂情况,性能较高,代码编写更简单结构更加简洁,而且对数据对象代码零侵入。
当然如果你还没有使用Java1.8或更高版本,那么你可以参考 匿名内部类 或 反射+注解 ,不过还是推荐 反射+注解 , Alibaba/easyexcel【https://github.com/alibaba/easyexcel】 对你来说会是不错的选择。
内存溢出
或 频繁的Full GC
,该如何解决? POI的使用对我们来说很常见,对下面两个概念应该并不陌生:
但是对于 eventmodel
和 streaming.SXSSFWorkbook
就很少接触了,它们是POI提供的专门用来解决内存占用问题的 low level API
(低级API),使用它们可以读写数据量非常大的Excel,同时可以避免 内存溢出
或 频繁的Full GC
。【 https://poi.apache.org/components/spreadsheet/how-to.html】
write(OutputStream stream)
方法写出内容时,再直接从临时内存写出到目标 OutputStream
。 SXSSFWorkbook
的使用会产生一些局限性。 实际上POI官网已经给了用户使用示例,而上述两个工具都只是做了自己的封装实现,使用者只需要拿来用就好。
<dependency> <groupId>com.github.liuhuagui</groupId> <artifactId>gridexcel</artifactId> <version>2.2</version> </dependency>
GridExcel.java提供了多种静态方法,可以直接使用,具体式例可参考测试代码(提供了测试数据和测试文件):
/** * 业务逻辑处理方式三选一: * 1.启用windowListener,并将业务逻辑放在该函数中。 * 2.不启用windowListener,使用get()方法取回全部数据集合,做后续处理。 * 3.readFunction函数,直接放在函数中处理 或 使用final or effective final的局部变量存放这写数据,做后续处理。 * 注意:使用EventModel时readFunction函数的输入为每行的cell值集合List<String>。 * @throws Exception */ @Test public void readXlsxByEventModel() throws Exception { InputStream resourceAsStream = Thread.currentThread().getContextClassLoader().getResourceAsStream("2007.xlsx"); GridExcel.readByEventModel(resourceAsStream,TradeOrder.class,ExcelType.XLSX) .window(2,ts -> System.out.println(JSON.toJSONString(ts)))//推荐在这里执行自己的业务逻辑 .process(cs ->{ TradeOrder tradeOrder = new TradeOrder(); tradeOrder.setTradeOrderId(Long.valueOf(cs.get(0))); Consultant consultant = new Consultant(); consultant.setConsultantName(cs.get(3)); tradeOrder.setConsultant(consultant); tradeOrder.setPaymentRatio(cs.get(16)); return tradeOrder; },1); } /** * 使用Streaming UserModel写出数据到Excel * @throws Exception */ @Test public void writeExcelByStreaming() throws Exception { GridExcel.writeByStreaming(TradeOrder.class) .head(writeFunctionMap())//对象字段到Excel列的映射 .createSheet() .process(MockData.data())//模拟数据。在这里设置业务数据集合。 .write(FileUtils.openOutputStream(new File("/excel/test.xlsx"))); }
Use user model to read excel file. userModel ——
Use event model to read excel file. eventModel ——
Use user model to write excel file. userModel ——
Use API-compatible streaming extension of XSSF to write very large excel file. streaming userModel——
在使用工具过程中出现问题,有功能添加或改动需求的可以向作者提Issue: https://github.com/liuhuagui/gridexcel/issues
如有疑问可以联系作者:799600902@qq.com