java Excel导入导出,基于XML的实现,easy-excel使用

来源:互联网 发布:c语言while e 编辑:程序博客网 时间:2024/06/03 20:10

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

jar包版本链接: 链接: http://pan.baidu.com/s/1sl8gbiX 密码: 5m8d

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

下面有如下的几个模型

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

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

有如下的Excel文件格式,需要映射成学生实体类 
这里写图片描述

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

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

下面使用easy-excel进行导入

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

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

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

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

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

标签解释: 
配置了一个id为student的映射,要映射对应的JavaBean实体为 StudentModel;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

运行结果:

[共导出【10】条数据][本次批次号为:XXX]StudentModel [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]]]StudentModel [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]StudentModel [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]]]StudentModel [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]StudentModel [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]]]StudentModel [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]StudentModel [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]]]StudentModel [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]StudentModel [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]]]StudentModel [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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

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

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

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

关于导入配置的一个很重要的属性:resolveFieldValueConverterName

在上面并没有配置它,这个属性就是可以解决之前我标记为红色的问题。那么它是什么?

它是一个ResolveFieldValueConverter接口的实现类:假设我们的Excel中有地址,或者结合业务中可能是其他,都是需要查询数据库,或者经过更复杂的业务逻辑进行校验的,那么我们可以配置它。我们来观察这个接口做了什么事?

/** * 解析Excel值解析接口 * @author lisuo * */public interface ResolveFieldValueConverter {    /**     * 操作类型,导入或导出     */    enum Type {        EXPORT, IMPORT    }    /**     * 解析配置中Field元素 处理后的值     * @param bean Excel配置的JavaBean对象     * @param value Excel原值     * @param fieldValue FieldValue信息     * @param type 导入或导出     * @param rowNum 行号     * @return 解析结果对应的value     * @throws Exception     */    public Object resolveFieldValue(Object bean,Object value, FieldValue fieldValue, Type type,int rowNum) throws Exception;}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

核心只有一个方法resolveFieldValue

我们可以自定义它的实现类,然后把全类名注册到 resolveFieldValueConverterName 属性上

如:假设创建人是需要查询用户表进行校验,如果没有则不允许导入,我们则可以在自定义的实现类抛出一个异常, 可以精准的提示用户多少行,哪一个字段【标题】的值错误了。

这里不在展示代码了,具体查看http://git.oschina.net/lis1314/easy-excel中的源代码;

<field name="createUser" title="创建人"         resolveFieldValueConverterName="org.easy.excel.test.converter.CreateUserFieldValueConverter"/>
  • 1
  • 2
  • 1
  • 2

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

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

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

运行结果生成文件: 
这里写图片描述

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

导出的一些疑问?支持样式么?支持在标题之前创建点其他的信息么?

目前支持cell宽度设置,sheet名称自定义,标题背景颜色,标题字体颜色,标题对齐方式,单元格样式是否与标题统一样式,默认cell对齐方式;

对应相关标签:

columnWidth,sheetname,titleBgColor,titleFountColor,align,enableStyle,defaultAlign。

支持导出前自定义头信息。

–上面红色标签部分为最新加入标签

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

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

<!-- Excel上下文  --><bean id="excelContext" class="org.easy.excel.ExcelContext">    <constructor-arg>        <!--你自己的excel配置文件路径,不要使用classpath前缀,底层使用的classPathResource-->                <value>excel-config.xml</value>    </constructor-arg></bean><!-- 初始化SpringUtil --><bean id="springUtil" class="org.easy.util.SpringUtil"/>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

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

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

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

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

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

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

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

decimalFormatPattern:Number格式处理

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

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

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

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