java Excel导入导出,基于XML的实现,easy

项目地址:http://git.oschina.net/lis1314/easy-excel

springboot 使用例子:https://gitee.com/lis1314/easy-excel-demo

2020年12月23日修改功能

https://gitee.com/lis1314/easy-excel/releases/v1.2.1

limit 限制导入条数,超出抛出ExcelException+中文描述

after 指定当前字段日期必须在指定的字段日期之后(校验)

--------------------------------------常见问题回答--------------------------------------

底层使用的poi dom解析,既一次性把文件读取到内容,如果文件内容特别庞大不建议使用本框架,我目前接触的需求数据量最多在20万以内,超过这个建议去看下阿里的easyexcel,那为啥要看我这个呢?因为我开源早啊。。还有就是,上手简单,类似spring的XML配置,小白入门级,快速开发,对于传统的场景是能够满足的。

支持单元格合并吗?不支持

支持集合吗?支持,可以使用spring ognl表达式配置,假设有3个列,备注1、备注2、备注3,可以在bean中配置一个list去接受

bean.list[0]=备注1,bean.list[1]=备注2,bean.list[1]=备注3

支持多个sheet页导入吗?不支持,只能支持指定使用其中哪个sheet页,以索引的方式指定。

支持导出多个sheet页吗?不支持

目前对于导出的样式支持如下:

1、可以使用自定义的模板导出数据,但是导出sheet页的内容不能指定,可以在sheet1写一些描述,sheet2让框架自己生成内容和数据

2、支持设置标题背景颜色、字体颜色、宽度、对齐方式,支持数据列的对齐方式、宽度,可以让数据列和标题的样式一致、也可以不一致

3、新增支持使用forceText=true强制设置单元格样式为text(避免用户修改数据使用科学计数法,以及日期自动把-替换成/等问题)

--------------------------------------正文描述--------------------------------------

使用easy-excel 完成Excel导入导出功能

下面有如下的几个模型

学生模型,图书模型,作者模型

1//学生模型 2public class StudentModel { 3 4 /** ID */ 5 protected String id; 6 /** 创建时间 */ 7 protected Date createTime; 8 /** 姓名 */ 9 private String name; 10 /** 年龄 */ 11 private Integer age; 12 /** 学号 */ 13 private String studentNo; 14 /** 创建人 */ 15 private String createUser; 16 /** 创建人ID */ 17 private String createUserId; 18 /** 状态 */ 19 private Integer status; 20 /** 图书信息 */ 21 private BookModel book; 22 //略 getter setter... 23} 24//图书模型 25public class BookModel { 26 /** 图书名称 */ 27 private String bookName; 28 /** 作者信息 */ 29 private AuthorModel author; 30 //略 getter setter... 31} 32//作者模型 33public class AuthorModel { 34 /** 作者姓名 */ 35 private String authorName; 36 //略 getter setter... 37}

有如下的Excel文件格式,需要映射成学生实体类

那么使用easy-excel 如何导入呢?

前两行是属于不规则的数据,从标题行以后才是规则的数据,也就是从规则数据之后才能正常转换成JavaBean。

下面使用easy-excel进行导入

1、下载easy-excel,引入到自己的工程,它是一个jar的maven工程,直接添加依赖即可

http://git.oschina.net/lis1314/easy-excel

2、具体实现代码,首先编写配置文件:excel-config.xml

1<excels> 2<excel id="student" class="org.easy.excel.test.model.StudentModel"> 3 <field name="id" title="ID"/> 4 <field name="name" title="学生姓名"/> 5 <field name="age" title="年龄" isNull="false" regex="^[1-9]d*$" regexErrMsg="必须是数字"/> 6 <field name="studentNo" title="学号" isNull="false" /> 7 <field name="createTime" title="创建时间" pattern="yyyy-MM-dd,yyyy/MM/dd"/> 8 <field name="status" title="状态" format="1:正常,0:禁用,-1:无效" /> 9 <field name="createUser" title="创建人"/> 10 <!-- 复杂对象 --> 11 <field name="book.bookName" title="图书名称" /> 12 <field name="book.author.authorName" title="作者名称"/> 13</excel> 14</excels>

解释,每个excel表示一种Excel文件到JavaBean的映射规则,该规则可以是导入和导出

标签解释

配置了一个id为student的映射,要映射对应的JavaBean实体为 StudentModel

<excel id="student" class="org.easy.excel.test.model.StudentModel">

excel文件中标题为ID的列,把它的值映射到 StudentModel .id属性上

<field name="id" title="ID"/>

isNull属性,表示在excel文件中标题为【年龄】的单元格的值不能为空,并且符合正则【 regex 】,当正则校验没有通过时,会提示【xxx行,[ 年龄 ]必须是数字,如果为空同理,xxx行[年龄]不能为空】

<field name="age" title="年龄" isNull="false" regex="^[1-9]d*$" regexErrMsg="必须是数字"/>

pattern:不做过多解释,SimpleDateFormat(pattern),导入数据时字符串的值如何转换成日期

支持多种映射pattern,使用【英文逗号】进行分割

<field name="createTime" title="创建时间" pattern="yyyy-MM-dd,yyyy/MM/dd"/>

format属性,观察上面的excel文件结构会发现状态列是中文,那么导入时,可能javaBean中并不是中文,而是数字或其他,那么如何把它转换成数字呢?format就是做这个事情的。导入时它会以【,分割:前面的作为导入时使用的值,:后面的作为导出时使用的值】:后面值进行逆推,导出时同理。思考一个问题?如果这个值不确定如何解决,或者这个值需要到数据库校验?比如是个城市地址,这个时候是需要查询数据库进行比对的,如果地址不存在则抛出错误提示信息的,就说这么多,easy-excel已经做好了,支持自定义的cell拦截器可以解决对应标签cellInterceptor

<field name="status" title="状态" format="1:正常,0:禁用,-1:无效" />

把excel文件标题为【图书名称】的值映射到 StudentModel 类中的book类中的bookName属性上

<field name="book.bookName" title="图书名称" />

-----------不每条配置都解释了,明白标签的作用我想大家能看懂。下面展示Java代码

1public class ImportTest { 2 public static void main(String[] args) throws Exception { 3 //准备excel文件流 4 InputStream excelStream = new FileInputStream("C:/Users/Administrator/Desktop/stu.xlsx"); 5 //创建excel上下文实例,它的构成需要配置文件的路径 6 ExcelContext context = new ExcelContext("excel-config.xml"); 7 //按照xml配置中id为student的配置形式读取excel文件,并转换成StudentModel 8 //这里的第二个参数是值,标题是第几行开始,之前也说了标题之前的数据并不是规则的数据 9 ExcelImportResult result = context.readExcel("student", 2,excelStream); 10 //打印导入结果,查看标题之前不规则的数据 11 List<List<Object>> header = result.getHeader(); 12 System.out.println(header.get(0)); 13 System.out.println(header.get(1)); 14 //查看学生集合导入结果 15 List<StudentModel> students = result.getListBean(); 16 for(StudentModel stu:students){ 17 System.out.println(stu); 18 } 19 } 20}

运行结果:

1[共导出【10】条数据] 2[本次批次号为:XXX] 3StudentModel [id=1, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三0, age=20, studentNo=Stu_0, createUser=王五0, createUserId=null, status=1, book=BookModel [bookName=Thinking in java, author=AuthorModel [authorName=Bruce Eckel]]] 4StudentModel [id=2, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三1, age=21, studentNo=Stu_1, createUser=王五1, createUserId=null, status=0, book=null] 5StudentModel [id=3, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三2, age=22, studentNo=Stu_2, createUser=王五2, createUserId=null, status=1, book=BookModel [bookName=Thinking in java, author=AuthorModel [authorName=Bruce Eckel]]] 6StudentModel [id=4, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三3, age=23, studentNo=Stu_3, createUser=王五3, createUserId=null, status=0, book=null] 7StudentModel [id=5, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三4, age=24, studentNo=Stu_4, createUser=王五4, createUserId=null, status=1, book=BookModel [bookName=Thinking in java, author=AuthorModel [authorName=Bruce Eckel]]] 8StudentModel [id=6, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三5, age=25, studentNo=Stu_5, createUser=王五5, createUserId=null, status=0, book=null] 9StudentModel [id=7, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三6, age=26, studentNo=Stu_6, createUser=王五6, createUserId=null, status=1, book=BookModel [bookName=Thinking in java, author=AuthorModel [authorName=Bruce Eckel]]] 10StudentModel [id=8, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三7, age=27, studentNo=Stu_7, createUser=王五7, createUserId=null, status=0, book=null] 11StudentModel [id=9, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三8, age=28, studentNo=Stu_8, createUser=王五8, createUserId=null, status=1, book=BookModel [bookName=Thinking in java, author=AuthorModel [authorName=Bruce Eckel]]] 12StudentModel [id=10, createTime=Sat Jun 18 00:00:00 GMT+08:00 2016, name=张三9, age=29, studentNo=Stu_9, createUser=王五9, createUserId=null, status=0, book=null]

导入结束,完美导入,其实上面的代码有些都很多余,那么核心的代码是哪几行?

1//准备excel文件流 2InputStream excelStream = new FileInputStream("C:/Users/Administrator/Desktop/stu.xlsx"); 3//创建excel上下文实例,它的构成需要配置文件的路径 4ExcelContext context = new ExcelContext("excel-config.xml"); 5//按照xml配置中id为student的配置形式读取excel文件,并转换成StudentModel 6//这里的第二个参数是值,标题是第几行开始,之前也说了标题之前的数据并不是规则的数据 7ExcelImportResult result = context.readExcel("student", 2,excelStream);

只有准备数据、创建上下文、读取excel。。通常在真实的常见创建上下文都可以省略了,因为它会交给spring容器管理,整个jvm中,只保持一个实例就够了。

如何使用easy-excel 进行导出?

之前的配置信息完全不变,直接上java代码

1public class ExportTest { 2 public static void main(String[] args) throws Exception { 3 //准备excel输出流 4 OutputStream ops = new FileOutputStream("C:/Users/Administrator/Desktop/exportStudent.xlsx"); 5 //创建excel上下文实例,它的构成需要配置文件的路径 6 ExcelContext context = new ExcelContext("excel-config.xml"); 7 //获取POI创建结果 8 Workbook workbook = context.createExcel("student",getStudents()); 9 workbook.write(ops); 10 ops.close(); 11 workbook.close(); 12 } 13 14 //获取模拟数据,数据库数据... 15 public static List<StudentModel> getStudents(){ 16 int size = 10; 17 List<StudentModel> students = new ArrayList<>(size); 18 for(int i=0;i<size;i++){ 19 StudentModel stu = new StudentModel(); 20 stu.setId(""+(i+1)); 21 stu.setName("张三"+i); 22 stu.setAge(20+i); 23 stu.setStudentNo("Stu_"+i); 24 stu.setCreateTime(new Date()); 25 stu.setStatus(i%2==0?1:0); 26 stu.setCreateUser("王五"+i); 27 28 //创建复杂对象 29 if(i % 2==0){ 30 BookModel book = new BookModel(); 31 book.setBookName("Thinking in java"); 32 AuthorModel author = new AuthorModel(); 33 author.setAuthorName("Bruce Eckel"); 34 book.setAuthor(author); 35 stu.setBook(book); 36 } 37 38 students.add(stu); 39 } 40 return students; 41 } 42}

运行结果生成文件:

其他更多导出示例参看http://http://git.oschina.net/lis1314/easy-excel中的源代码

补充一点WEB环境或spring mvc环境可以使用org.easy.excel.util.ExcelDownLoadUtil进行下载,当然也可以使用内部创建好的spring mvc视图

**导出目前支持的功能、**样式上支持列宽度,背景颜色(标题和列可以不一样)支持在创建标题之前额外创建一些表头数据

导出样式相关标签

sheetname    导出时,sheet页的名称,可以不设置

requiredTag  导出的列如果是必填项,标题前面会有一个*

defaultAlign    导出整体的默认对齐方式【center,left,right】

defaultColumnWidth    导出时列整体的默认宽度

<excel id="studentMap" class="map" requiredTag="true" sheetname="测试学生列表" defaultAlign="center" defaultColumnWidth="5000">

针对单列或标题样式的设置标签

columnWidth    列宽

align    对齐方式

titleFountColor    标题字体颜色

titleBgColor    背景颜色

uniformStyle    标题列和数据列样式是否一致,true为一致,默认是false

<field name="age" title="年龄" columnWidth="3000" align="right" titleFountColor="red" titleBgColor="dark_blue" uniformStyle="true"/>

补充:如何在自己工程中与spring无缝集成以及使用?

在你的spring.xml配置文件中加入

1<!-- Excel上下文 --> 2<bean id="excelContext" class="org.easy.excel.ExcelContext"> 3 <constructor-arg> 4 <!--你自己的excel配置文件路径,支持classpath前缀,如果不写前缀,默认为classpath:--> 5 <value>excel-config.xml</value> 6 </constructor-arg> 7</bean>

关于海量数据导出问题,看如下代码,非常easy

1/*** 2* 导出测试,分多次导出 3* @throws Exception 4*/ 5@Test 6public void testExporPart()throws Exception{ 7 //需求概述.数据量较大,可能大批量数据导出,会对DB造成压力,这里分批次检索数据,一部分一部分向Excel中写 8 OutputStream ops = new FileOutputStream(path); 9 ExcelExportResult exportResult = context.createExcelForPart(excelId,getStudents()); 10 //假设这是第二次从数据库或其他平台查询到到数据 11 exportResult.append(getStudents()); 12 //第n次.... 13 exportResult.append(getStudents()); 14 exportResult.append(getStudents()); 15 exportResult.append(getStudents()); 16 exportResult.append(getStudents()); 17 Workbook workbook = exportResult.build(); 18 workbook.write(ops); 19 ops.close(); 20 workbook.close(); 21}

2016-09-02加入以下4个标签:

otherConfig:如下面XML注释,我遇到额需求是处理字典,只有Key不同,其他逻辑一模一样。那么我通过otherConfig达到动态传参的功能。

1<!-- 当一个转换器处理多个字段时,可能逻辑一样,只有个别的参数不一样,那么可以使用otherConfig,动态传递 --> 2<field name="createUser" title="创建人" cellValueConverter="org.easy.excel.test.converter.CreateUserFieldValueConverter" otherConfig="Test动态传递的一个变量"/>

defaultValue:无论是导入还是导出都给一个默认值

1<!-- defaultValue,无论是导入还是导出当值为空给一个默认值 --> 2<field name="book.price" title="图书价格" columnWidth="6000" defaultValue="0.00"/>

decimalFormatPattern:Number格式处理

1<!--Number类型进行处理,比如(12345.12),转换成(123,45.12),导入的规则是从字符到Number,导出是从Number到字符,基于标准的java.text.DecimalFormat --> 2<field name="book.price" title="图书价格" columnWidth="6000" decimalFormatPattern="###,##0.00" defaultValue="0.00"/>

roundingMode:结合上么的decimalFormatPattern标签,对于小数位数处理方案进行配置,默认向下取整

1<!-- 当处理字符时,假设保留2位小数,那么遇到3位甚至更多的位数如何处理?通过该配置可以指定处理方式,默认向下取整 --> 2<field name="book.price" title="图书价格" columnWidth="6000" decimalFormatPattern="###,##0.00" roundingMode="up" defaultValue="0.00"/>

2017-03-21 变更:

主要对工程进行了重构,对一些包重新进行了划分以及命名

重命名resolveFieldValueConverterName配置为cellValueConverter

在ExcelDefinition中增加了

sheetIndex属性,支持指定sheet形式读取内容

增加了

ExcelUtil ,用于读取没有规则的Excel文件

ExcelDownLoadUtil,用于WEB环境Excel文件的下载

SpringMvcExcelView,用于WEB环境使用SpringMvc轻松实现下载

与SpringMvc整合

正常整合,把org.easy.util.view.SpringMvcExcelView添加到SpringMvc容器中,使用标准的Spring自定义视图跳转即可,参看org.easy.excel.view.SpringMvcExcelView这个类的注释部分

2017-03-30 变更:支持多行导入校验

例子代码

1//具体参看 ImportMultiValidateTest 测试类 2ExcelImportResult result = context.readExcel(excelId, titleIndex, excelStream,true); 3//通过导入结果集的hasErrors方法判断 4if(result.hasErrors()){ 5 System.out.println("导入包含错误,下面是错误信息:"); 6 for (ExcelError err:result.getErrors()) { 7 System.out.println(err.getErrorMsg()); 8 } 9} 10 11//运行结果 12导入包含错误,下面是错误信息: 13[1],[创建时间][D]不能转换成日期,正确的格式应该是:[yyyy-MM-dd] 14[1],[状态][不正常]取值错误 15[2],[作者名称]不能为空 16[4],[作者名称]不能为空

2020-05-28 变更:

1、增加功能:支持导出数据使用固定模板追加sheet页的方式生成workbook(excel)

2、修改导入标题校验规则,如果配置的列isNull属性为false,强制校验标题。非必填项不校验标题

3、增加功能:新增requiredTag配置true或false,如果配置了属性为true,必填项在导出数据时,标题前面会自动带有*号标红提示

4、修复BUG:对于长期不使用的应用,导出excel poi底层的临时文件被删除之后,导致系统不能正常导出数据

详情代码查看:org.easy.excel.config.PoiTempFileCreationStrategy

5、异常统一:抛出异常为ExcelException和ExcelDataException,任何方法不再抛出编译异常

6、重构了CellValueConverter接口,不再抛出编译时异常,增量了DefaultCellValueConverter类(可以继承该类,拓展一些自定义的转换器)

7、修复BUG:ExcelDownLoadUtil个别浏览器下载中文乱码问题

8、删除org.easy.util.SpringUtil,整合spring只需要配置org.easy.excel.ExcelContext即可

9、底层反射属性转换统一使用org.springframework.beans.DirectFieldAccessor 处理,类型转换使用org.springframework.core.convert.support.DefaultConversionService

更多信息请参考源代码工程中的org.easy.excel.test包下的几个类

点赞
收藏

评论区

加载中...

相关推荐

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java项目http变更https

1\.创建keystore文件执行keytoolgenkeyvaliastomcatkeyalgRSA  validity3650 keystorec:\\tomcat.keystoredname"CNlocalhost,OUcn,Ocn,Lcn,STcn,ccn"storepasspassw

Spring+spring mvc+redis+mybatis+PageHelper

iBase4J是基于java的开源框架搭建分布式系统架构,使用到的开源框架有:SpringMVC,Mybatis,Redis数据和session缓存,分布式,单点登录,shiro权限管理,dubbo扩展,Rest等等;http://git.oschina.net/iBase4J/iBase4J(http://git.oschina.net/iBase4J

SpringBoot 打包 jar 后执行 thymeleaf 提示找不到模版

基于懒,花了点时间研究了一下SpringBoot,个人觉得也就是一个懒人版的SS框架我的demogit地址:http://git.oschina.net/zgdhd/myspringboot(http://git.oschina.net/zgdhd/myspringboot) 希望里面的注释能帮助初学的师弟问题:打包成jar

JFBlog 1.0 maven版本开源

JFBlogmaven此项目是JFBlog的Maven版本。1、源码支持JFBlog源码http://git.oschina.net/realfighter/JFBlog(http://git.oschina.net/realfighter/JFBlog)JFBl

EasyExcel写入百万级数据到多sheet

EasyExcel是什么?快速、简单避免OOM的java处理Excel工具一、项目需求    从mongo库中查询数据,导出到excel文件中。但是动态导出的excel有多少列、列名是什么、有多少sheet页都需要动态获取。所以生成的excel也必须是动态生成,不能通过注解配置对象映射。而且写入的数据量,有可能达到100W级,使用传统的PO