基于Java8实现提高Excel读写效率

在POI的使用过程中,对大多数API User来说经常面临两个问题,这也是GridExcel致力解决的问题。

问题1. 仅使用简单的导入导出功能,但每次业务的数据对象结构不同,需要重新编写处理方法,很麻烦!

解决方法

将Excel处理逻辑抽取出来,封装成工具类。

封装条件

与大多数Java API一样,POI把更多的精力放在高级功能的处理上,比如Formula(公式)、Conditional Formatting(条件格式)、Zoom(缩放)等。对于仅仅做数据导入导出功能的API User,很少使用这些高级特性,这允许API用户对POI的使用进行简单的封装。

封装方式

无论是读是写,我们都需要解决Excel中的Columns(列)与Java数据对象Fields(字段)的映射关系,将这种映射关系作为参数(Map对象HashMap或LinkedHashMap),传递给工具类。

对于Columns不难理解,它可以是有序的数字或字母,也可以是其它字符串用来作为首行,表示该列数据的含义。

对于Fields,它的处理需要兼容复杂情况,如下:

  • 查询字段时出现异常
  • 字段或单元格的值为null
  • 该列的值可能对应关联对象、甚至是关联集合中的某个字段值
  • 字段或单元格的值需要做特殊处理,例如value == true?完成:失败;

反射

首先想到,也是大多数封装者都在使用的方式是就是Reflection API,从上文 函数编程 章节我们了解到,反射重量级,会降低代码的性能,同时对复杂情况的处理支持性不够好。

反射+注解

这种方式可以更好的支持复杂情况,但是反射依然会降低性能,同时注解对数据对象会造成代码侵入,而且对该工具类封装者的其他使用者无疑会增加学习成本。

匿名内部类

这种方式也可以很好的支持复杂情况,但是使用匿名内部类的语法显然患有“垂直问题”(这意味着代码需要太多的线条来表达基本概念),太过冗杂。至于性能,应该也不如直接传递函数来的快吧。

函数接口(Lambda)

这种方式是基于第5条方法调用的字节码指令invokeDynamic实现的,直接传递函数代码块,很好的支持复杂情况,性能较高,代码编写更简单结构更加简洁,而且对数据对象代码零侵入。

当然如果你还没有使用Java1.8或更高版本,那么你可以参考匿名内部类或反射+注解,不过还是推荐反射+注解,Alibaba/easyexcel【https://github.com/alibaba/easyexcel】对你来说会是不错的选择。

问题2. Excel导入或导出数据量比较大,造成内存溢出或频繁的Full GC,该如何解决?

解决方法

  • 读Excel —— eventmodel
  • 写Excel —— streaming.SXSSFWorkbook

原理

POI的使用对我们来说很常见,对下面两个概念应该并不陌生:

  • HSSFWorkbook(处理97(-2007) 的.xls)
  • XSSFWorkbook(处理2007 OOXML (.xlsx) )

但是对于eventmodel和streaming.SXSSFWorkbook就很少接触了,它们是POI提供的专门用来解决内存占用问题的low level API(低级API),使用它们可以读写数据量非常大的Excel,同时可以避免内存溢出或频繁的Full GC。【https://poi.apache.org/components/spreadsheet/how-to.html】

eventmodel,用来读Excel,并没有将Excel整个加载到内存中,而是允许用户从InputStream每读取一些信息,就交给回调函数或监听器,至于丢弃,存储还是怎么处理这些内容,都交由用户。

streaming.SXSSFWorkbook,用来写Excel(是对XSSFWorkbook的封装,仅支持.xlsx),通过滑动窗口来实现,只在内存中保留滑动窗口允许存在的行数,超出的行Rows被写出到临时文件,当调用write(OutputStream stream)方法写出内容时,再直接从临时内存写出到目标OutputStream。SXSSFWorkbook的使用会产生一些局限性。

  • Only a limited number of rows are accessible at a point in time.
  • Sheet.clone() is not supported.
  • Formula evaluation is not supported

解决途径

https://github.com/liuhuagui/gridexcel 基于Java函数编程(Lambda),支持流式API,使用环境Java1.8或更高,学习成本:

Lambda

https://github.com/alibaba/easyexcel 基于反射+注解+监听器,使用环境Java1.6或以上,学习成本:模型注解
实际上POI官网已经给了用户使用示例,而上述两个工具都只是做了自己的封装实现,使用者只需要拿来用就好。

快速使用

<dependency>
  <groupId>com.github.liuhuagui</groupId>
  <artifactId>gridexcel</artifactId>
  <version>2.2</version>
</dependency>

GridExcel.java

GridExcel.java提供了多种静态方法,可以直接使用,具体式例可参考测试代码(提供了测试数据和测试文件):

https://github.com/liuhuagui/gridexcel/blob/master/src/test/java/ReadTest.java

https://github.com/liuhuagui/gridexcel/blob/master/src/test/java/WriteTest.java

流式API

/**
 * 业务逻辑处理方式三选一:
 * 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")));
 }

ReadExcel

ReadExcelByUserModel

Use user model to read excel file. userModel ——

缺点:内存消耗大,会将excel信息全部加载到内存再进行处理。

优点:现成的API,使用和理解更简单。

使用场景:可以处理数据量较小的Excel。

ReadExcelByEventModel

Use event model to read excel file. eventModel ——

缺点:没有现成的API,使用和理解较为复杂,适合中高级程序员(GridExcel的目标之一就是让EventModel的使用变得简单)

优点:非常小的内存占用,并没有在一开始就将所有内容加载到内存中,而是把主体内容的处理(存储,使用,丢弃)都交给了用户,用户可以自定义监听函数来处理这些内容。
使用场景:可以处理较大数据量的Excel,避免OOM和频繁FullGC

WriteExcel

WriteExcelByUserModel

Use user model to write excel file. userModel ——

缺点:会将产生的spreadsheets对象整个保存在内存中,所以write Excel的大小受到堆内存(Heap space)大小限制。

优点:使用和理解更简单。

使用场景:可以写出数据量较小的Excel。

WriteExcelByStreaming

Use API-compatible streaming extension of XSSF to write very large excel file. streaming userModel——

缺点:

  • 仅支持XSSF;
  • Sheet.clone() is not supported;
  • Formula evaluation is not supported;
  • Only a limited number of rows are accessible at a point in time.

优点:通过滑动窗口来实现,内存中只保留指定size of rows的内容,超出部分被写出到临时文件,write Excel的大小不再受到堆内存(Heap space)大小限制。

使用场景:可以写出非常大的Excel。

Issues

在使用工具过程中出现问题,有功能添加或改动需求的可以向作者提Issue:https://github.com/liuhuagui/gridexcel/issues

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java实现读取及生成Excel文件的方法

    本文实例讲述了Java实现读取及生成Excel文件的方法.分享给大家供大家参考,具体如下: 一.读取Excel文件 需要先下载poi-3.0.1-FINAL-20070705.jar(点击此处本站下载poi-3.0.1-FINAL-20070705.jar.) ExcelExamRead.java import java.io.File; import java.io.FileInputStream; import java.io.IOException; import org.apache.p

  • Java如何利用POI读取Excel行数

    这篇文章主要介绍了java如何利用POI读取Execel行数,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 java 利用poi 读excel文件的操作,读取总的数据行数一般是通过调用 sheet.getLastRowNum() ;可是这样有时候会出现一些问题,例如,当其中一行的数据的确都为空,可是其原本的格式还在,并没有连带删除,这样计算出来的行数就不真实(比真实的大),还有当出现空白行时(也即某一行没有任何数据,通过Row row = sh

  • JAVA如何读取Excel数据

    1.创建Maven项目在pom文件中添加依赖 <dependencies> <!-- 旧的 .xls --> <!--<dependency> <groupId>org.apache.poi</groupId> <artifactId>poi</artifactId> <version>3.17</version> </dependency>--> <!-- 新的 .x

  • java读取excel文件的两种方法

    本文实例为大家分享了Android九宫格图片展示的具体代码,供大家参考,具体内容如下 方式一: 借用 package com.ij34.util; /** * @author Admin * @date 创建时间:2017年8月29日 下午2:07:59 * @version 1.0 *@type_name myclass */ import java.io.File; import java.io.IOException; import jxl.Cell; import jxl.Sheet;

  • Java POI读取excel中数值精度损失问题解决

    描述: excel 单元格中,纯数字的单元格,读取后 后面会加上 .0 . 例如: 1 --> 1.0 而使用下面的方法,可能会对小数存在精度损失 cell.setCellType(CellType.STRING); //读取前将单元格设置为文本类型读取 例如: 2.2 --> 2.1999999997 目前的解决办法: 一. 将excel单元格改为文本类型 注意,直接修改单元格属性不管用, 使用 分列 的方式,可以实现将数值改为文本类型. 二. java处理 public class Com

  • Java 添加、修改、读取、复制、删除Excel批注的实现

    批注,是作者或审阅者给文档添加的注释或注解.通过查看批注,可以更加详细地了解某些文字的背景.除了直接添加文本信息外,还可为文本信息填充背景图片使其更具丰富性和美观性.本文将通过使用Java程序来演示如何在Excel文档中添加.修改.读取.复制和删除批注. 使用工具: Free Spire.XLS for Java(免费版) Jar文件获取及导入: 方法1:通过官方网站下载获取jar包.解压后将lib文件夹下的Spire.Xls.jar文件导入Java程序.(如下图) 方法2:通过maven仓库安

  • Java 添加、读取和删除 Excel 批注的操作代码

    批注是一种富文本注释,常用于为指定的Excel单元格添加提示或附加信息. Free Spire.XLS for Java为开发人员免费提供了在Java应用程序中对Excel文件添加和操作批注的功能. 本文将介绍如何使用 Free Spire.XLS for Java在Excel文档中添加,读取和删除批注. 安装 首先你需要下载 Spire.XLS JAR并将其作为依赖项添加到您的Java程序中.如果您使用的是maven,您需要将以下依赖项添加到您的pom.xml文件中. <repositorie

  • JAVA使用POI(XSSFWORKBOOK)读取EXCEL文件过程解析

    经过一番搜索发现,java操纵excel文件常用的有jxl和poi两种方式,孰好孰坏看自己需求而定. 其中最主要的区别在于jxl不支持.xlsx,而poi支持.xlsx 这里介绍的使用poi方式(XSSFWorkbook),实际上poi提供了HSSFWorkbook和XSSFWorkbook两个实现类.区别在于HSSFWorkbook是针对.xls文件,XSSFWorkbook是针对.xslx文件. 首先明确一下基本概念: 先创建一个工作簿,一个工作簿可以有多个工作表,一个工作表可以有多个行,一

  • Java利用POI读取、写入Excel的方法指南

    前言 Apache POI [1] 是用Java编写的免费开源的跨平台的 Java API,Apache POI提供API给Java程式对Microsoft Office格式档案读和写的功能.POI为"Poor Obfuscation Implementation"的首字母缩写,意为"简洁版的模糊实现". 做项目时经常有通过程序读取Excel数据,或是创建新的Excel并写入数据的需求: 网上很多经验教程里使用的POI版本都比较老了,一些API在新版里已经废弃,这里

  • 基于Java8实现提高Excel读写效率

    在POI的使用过程中,对大多数API User来说经常面临两个问题,这也是GridExcel致力解决的问题. 问题1. 仅使用简单的导入导出功能,但每次业务的数据对象结构不同,需要重新编写处理方法,很麻烦! 解决方法 将Excel处理逻辑抽取出来,封装成工具类. 封装条件 与大多数Java API一样,POI把更多的精力放在高级功能的处理上,比如Formula(公式).Conditional Formatting(条件格式).Zoom(缩放)等.对于仅仅做数据导入导出功能的API User,很少

  • 基于Python的接口自动化读写excel文件的方法

    引言 使用python进行接口测试时常常需要接口用例测试数据.断言接口功能.验证接口响应状态等,如果大量的接口测试用例脚本都将接口测试用例数据写在脚本文件中,这样写出来整个接口测试用例脚本代码将看起来很冗余和难以清晰的阅读以及维护,试想如果所有的接口测试数据都写在代码中,接口参数或者测试数据需要修改,那不得每个代码文件都要一一改动?.因此,这种不高效的模式不是我们想要的.所以,在自动化测试中就有个重要的思想:测试数据和测试脚本分离,也就是测试脚本只有一份,其中需要输入数据的地方会用变量来代替,然

  • 提高网页的效率 Use YSlow to know why your web Slow第1/2页

    所以网页的效率绝对是最值得关注的方面.虽然我们在 <如何提高网页的效率(上篇)--提高网页效率的14条准则>提到了如何提高网页效率的14条准则,但是如何知道我们现在的网页的效率到底如何?到底处于怎样一个级别?又有哪些方面做的不够好,需要改进呢?也许,你会说,问一下用户不就知道了吗?但是相比感性比例占据太大的用户感受而言,理性的工具和数据更具有说服力.本篇就将向你介绍一款评测网页效率的工具--YSlow(why slow,这个名字起的太好了). yslow YSlow是由Yahoo开发者团队发布

  • 分享50个提高PHP执行效率的技巧

    1.用单引号代替双引号来包含字符串,这样做会更快一些.因为PHP会在双引号包围的字符串中搜寻变量, 单引号则不会,注意:只有echo能这么做,它是一种可以把多个字符串当作参数的"函数"(译注:PHP手册中说echo是语言结构,不是真正的函数,故 把函数加上了双引号). 2.如果能将类的方法定义成static,就尽量定义成static,它的速度会提升将近4倍. 3.$row['id'] 的速度是$row[id]的7倍. 4.echo 比 print 快,并且使用echo的多重参数(译注:

  • 提高MySQL 查询效率的三个技巧第1/2页

    MySQL由于它本身的小巧和操作的高效, 在数据库应用中越来越多的被采用.我在开发一个P2P应用的时候曾经使用MySQL来保存P2P节点,由于P2P的应用中,结点数动辄上万个,而且节点变化频繁,因此一定要保持查询和插入的高效.以下是我在使用过程中做的提高效率的三个有效的尝试. l        使用statement进行绑定查询 使用statement可以提前构建查询语法树,在查询时不再需要构建语法树就直接查询.因此可以很好的提高查询的效率. 这个方法适合于查询条件固定但查询非常频繁的场合. 使

  • PHP基于文件锁解决多进程同时读写一个文件问题示例

    本文实例讲述了PHP基于文件锁解决多进程同时读写一个文件问题.分享给大家供大家参考,具体如下: 首先PHP是支持进程的而不支持多线程(这个先搞清楚了),如果是对于文件操作,其实你只需要给文件加锁就能解决,不需要其它操作,PHP的flock已经帮你搞定了. 用flock在写文件前先锁上,等写完后解锁,这样就实现了多线程同时读写一个文件避免冲突.大概就是下面这个流程 /* *flock(file,lock,block) *file 必需,规定要锁定或释放的已打开的文件 *lock 必需.规定要使用哪

  • C#基于COM方式读取Excel表格的方法

    本文实例讲述了C#基于COM方式读取Excel表格的方法.分享给大家供大家参考,具体如下: using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Data; using System.Linq; using System.Text; using System.Threading.Tasks; using System.Windows; using Sys

  • Sqlite 常用函数封装提高Codeeer的效率

    以下是频繁用到的Sqlite函数,内容格式相对固定,封装一下有助于提高开发效率(^_^至少提高Codeeer的效率了) 而且,我发现Sqlite中文资料比较少,起码相对其他找起来要复杂些,服务一下大众~ 我没有封装读取部分,因为数据库读取灵活性太大,封装起来难度也大,而且就算封装好了,也难以应付所有情况,还是建议根据实际情况设计代码逻辑. 创建: 复制代码 代码如下: /// <summary> /// Creat New Sqlite File /// </summary> //

  • 注册表简易优化法 大大提高ADSL工作效率

    我们在使用一件产品时总希望能发挥产品的最大功效.我们宽带上网也是同样心理,怎样才能使ADSL的速度最快,效果最好呢?在现有硬件条件下我们只能尽量优化!     Windows 系列在安装时,默认的是针对以太网的设置,在TCP/IP数据包传送过程中就会把一些无用功做在了分包和组合以适应ISP.虽然ADSL使用PPPoE协议,具有局域网的特点,但是又不完全等于局域网协议,所以还是有优化的余地,我们可以通过修改注册表中有关参数,使系统针对ADSL进行优化.具体的参数有MaxMTU.DefaultRcv

  • SpringBoot和Swagger结合提高API开发效率

    现在Web开发越来越倾向于前后端分离,前端使用AngularJS,React,Vue等,部署在NodeJS上,后面采用SpringBoot发布Rest服务,前后端进行分离.这样的架构灵活且特别适合大型团队的协作开发. 那么问题来了,因为前端都是和后端通过API进行交互的,那么前后端的Rest API的接口如何进行定义和沟通呢?首先想到的应该就是Swagger. 那么什么是Swagger,Swagger™的目标是为REST APIs 定义一个标准的,与语言无关的接口,使人和计算机在看不到源码或者看

随机推荐