EasyExcel

初识EasyExcel

Apache POI

Apache POI是Apache软件基金会的开源函式库,提供跨平台的Java API实现Microsoft Office格式档案读写。但是存在如下一些问题:

  • 学习使用成本较高

对POI有过深入了解的应该知道POI有SAX模式(Dom解析模式)。但SAX模式相对比较复杂,excel有03(xls文件)和07(xlsx文件)两种版本,两个版本数据存储方式截然不同,sax解析方式也各不一样。

POI的SAX模式的API可以一定程度的解决一些内存溢出的问题,但是POI还是有一些缺陷,比如07版Excel解压缩以及解压后存储都是在内存中完成的,内存消耗依然很大,一个3M的excel用POI的SAX解析,依然需要100M左右内存。

  • POI的内存消耗较大

大部分使用POI都是使用它的userModel模式。userModel的好处是上手容易、使用简单,然而userModel模式最大的问题是在于非常大的内存消耗,一个几兆的文件解析要用掉几百兆的内存。现在很多应用采用这种模式,之所以还正常在跑一定是并发不大,并发上来后一定会OOM或者频繁的full gc。

总体来说,简单写法重度依赖内存,复杂写法学习成本高。

EasyExcel

重写了POI对07版excel的解析。

easyexcel重写了POI对07版excel的解析,可以把内存消耗从100M左右降低到10M以内,并且再大的excel不会出现内存溢出,03版仍依赖POI的SAX模式。

下图为64M内存1分钟内读取75M(46万行25列)的excel(当然还有急速模式能更快,但是内存占用会在100M多一点)。
在这里插入图片描述
在上层做了模型转换的封装,让使用者更加简单方便。

特点

  • 在数据模式层面进行了封装,使用简单;
  • 重写了07版本的excel的解析代码,降低内存消耗,能有效避免OOM;
  • 只能操作excel;
  • 不能读取图片。

快速入门

简单读

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qrxqrx</groupId>
    <artifactId>easyexcel</artifactId>
    <version>1.0-SNAPSHOT</version>


    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <scope>provided</scope>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
    </dependencies>


</project>

log4j.properties

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n


### direct message to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=d:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=debug, stdout

Student

package com.qrxqrx.easyexcel.domain;

import lombok.Data;

import java.util.Date;

@Data
public class Student {

    private String name;
    private String gender;
    private Date birthday;
    private String id;

}

StudentListener

package com.qrxqrx.easyexcel.listener;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.qrxqrx.easyexcel.domain.Student;

//读取文档的监听器类
public class StudentListener extends AnalysisEventListener<Student> {

    // 每读一行内容,都会调用一次该对象的invoke方法,在invoke方法中可以操作使用读取到的数据
    // 参数student:每次读取到的数据封装到的对象
    @Override
    public void invoke(Student student, AnalysisContext analysisContext) {
        System.out.println(student);
    }

    // 读取完整个文档之后调用的方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

ExcelTest

package com.qrxqrx.easyexcel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.qrxqrx.easyexcel.domain.Student;
import com.qrxqrx.easyexcel.listener.StudentListener;
import org.junit.Test;

public class ExcelTest {

    /*
    * 工作簿:一个excel文件就是一个工作簿
    * 工作表:一个工作簿中可以有多个工作表(sheet)
    * */

    @Test
    public void test01() {
        // 获得工作簿对象
        //输入参数:要读的文件的路径、文件中每一行数据要存储到的实体的类型的class、
        // 读监听器(每读一行内容,都会调用一次该对象的invoke方法,在invoke方法中可以操作使用读取到的数据)
        ExcelReaderBuilder readWorkBook = EasyExcel.read("data.xlsx", Student.class, new StudentListener());

        // 获得工作表对象
        ExcelReaderSheetBuilder sheet = readWorkBook.sheet();

        // 读取工作表中内容
        sheet.doRead();
    }

}

data.xlsx:
在这里插入图片描述

简单写

Student

package com.qrxqrx.easyexcel.domain;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import lombok.Data;

import java.util.Date;

@Data
// @ContentRowHeight() // 内容行高
public class Student {

    @ColumnWidth(20)
    @ExcelProperty(value = "学生姓名", index = 1)
    private String name;

    @ExcelProperty(value = "学生性别", index = 3)
    private String gender;

    @ColumnWidth(20)
    @ExcelProperty(value = "学生生日", index = 2)
    private Date birthday;

    @ExcelProperty(value = "ID", index = 4)
    private String id;

}

ExcelTest

package com.qrxqrx.easyexcel;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.qrxqrx.easyexcel.domain.Student;
import com.qrxqrx.easyexcel.listener.StudentListener;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Date;

public class ExcelTest {

    /*
    * 工作簿:一个excel文件就是一个工作簿
    * 工作表:一个工作簿中可以有多个工作表(sheet)
    * */

    @Test
    public void test01() {
        // 获得工作簿对象
        //输入参数:要读的文件的路径、文件中每一行数据要存储到的实体的类型的class、
        // 读监听器(每读一行内容,都会调用一次该对象的invoke方法,在invoke方法中可以操作使用读取到的数据)
        ExcelReaderBuilder readWorkBook = EasyExcel.read("data.xlsx", Student.class, new StudentListener());

        // 获得工作表对象
        ExcelReaderSheetBuilder sheet = readWorkBook.sheet();

        // 读取工作表中内容
        sheet.doRead();
    }

    @Test
    public void test02() {
        // 工作簿对象
        ExcelWriterBuilder writeWorkBook = EasyExcel.write("data-w.xlsx", Student.class);

        // 工作表对象
        ExcelWriterSheetBuilder sheet = writeWorkBook.sheet();

        ArrayList<Student> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Student student = new Student();
            student.setName("qrx"+i);
            student.setGender("男");
            student.setBirthday(new Date());
            list.add(student);
        }

        sheet.doWrite(list);

    }

}

data-w.xlsx:
在这里插入图片描述

常用API及注释

常用类

  • EasyExcel入口类,用于构建各种对象,开始各种操作;
  • ExcelReaderBuilder构建出一个ReadWorkbook对象,即一个工作簿对象,对应的是一个Excel文件;
  • ExcelWriterBuilder构建出一个WriteWorkbook对象,即一个工作簿对象,对应的是一个Excel文件;
  • ExcelReaderSheetBuilder构建出一个ReadSheet对象,即一个工作表对象,对应的Excel中的每个sheet,一个工作簿可以有多个工作表;
  • ExcelWriterSheetBuilder构建出一个WriteSheet对象,即一个工作表对象,对应的Excel中的每个sheet,一个工作簿可以有多个工作表;
  • ReadListener在每一行读取完毕后都会调用ReadListener来处理数据,可以把调用service的代码写在其invoke方法内部;
  • WriteHandler在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据,对使用者透明不可见;
  • 所有配置都是继承的,Workbook的配置会被Sheet继承。所以在用EasyExcel设置参数的时候,在sheet()方法之前作用域是整个workbook的所有sheet,之后针对单个sheet。

读取时的注解

@ExcelProperty

使用位置:标准作用在成员变量上。
可选属性

属性名含义说明
index对应excel表中的列数默认-1,建议从0开始
value对应excel表中的列头
converter成员变量转换器自定义转换器需要实现Converter接口

使用效果:index属性可以指定当前字段对应excel中的哪一列,可以根据列名value去匹配,也可以不写。如果不使用@ExcelProperties注解,成员变量从上到下的顺序,对应表格中从左到右的顺序。
使用建议:要么全部不写,要么全部用index,要么全部用value去匹配,尽量不要三个混着用。

@ExcelIgnore

标注在成员变量上,默认所有字段都会和excel去匹配,加了这个注解会忽略该字段

@DateTimeFormat

标注在成员变量上,日期转换,代码中用String类型的成员变量去接收excel中日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat。

@DateTimeFormat("yyyy-MM-dd")
private Date birthday;

@NumberFormat

标注在成员变量上,数字转换,代码中用String类型的成员变量去接收excel数字格式的数据会调用这个注解,里面的value参照java.text.DecimalFormat。

ExcelIngoreUnannotated

标注在类上。
不标注该注解时,默认类中所有成员变量都会参与读写,无论是否在成员变量上加了@ExcelProperty的注解。
标注该注解后,类中的成员变量如果没有标注@ExcelProperty注解将不会参与读写。

读取时的通用参数

ReadWorkbook,ReadSheet都会有的参数,如果为空,默认使用上级。

  • converter转换器,默认加载了很多转换器,也可以自定义。
  • readListener监听器,在读取数据的过程中会不断的调用监听器。
  • headRowNumber指定需要读表格的列头行数。默认有一行头,也就是认为第二行开始起为数据。
  • head与clazz二选一。读取文件头对应的列表,会根据列表匹配数据。建议使用class,就是文件中每一行数据对应的代码中的实体类型。
  • clazz与head二选一,读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。
  • autoTrim字符串、表头等数据自动trim。
  • password读的时候是否需要使用密码。

ReadWorkbook(工作簿对象)参数

  • excelType当前excel的类型,读取时会自动判断,无需设置。
  • inputStream与file二选一,建议使用file。
  • file与inputStream二选一,读取文件的文件。
  • autoCloseStream自动关闭流。
  • readCache默认小于5M用内存,超过5M会使用EhCache,不建议使用这个参数。
  • useDefaultListener默认会加入ModelBuildEventListener来转换成传入class的对象,设置成false后将不会协助转换对象,自定义的监听器会接收到Map<Integer,CellData>对象,如果还想继续监听到class对象,请调用readListener方法,加入自己定义的beforeListener、ModelBuildEventListener、自定义的afterListener即可。

ReadSheet(工作表对象)参数

  • sheetNo需要读取Sheet的编号,建议使用这个指定读取哪个Sheet
  • sheetName根据名字去匹配Sheet,excel2003不支持根据名字去匹配

写入时的注解

@ExcelProperty

使用位置:标准作用在成员变量上
可选属性

属性名含义说明
index对应excel表中的列数默认-1,指定时建议从0开始
value对应excel表中的列头
converter成员变量转换器自定义转换器需要实现Converter接口

使用效果:index指定写到第几列,如果不指定则根据成员变量位置排序;value指定写入的列头,如果不指定则使用成员变量的名字作为列头;如果要设置复杂的头,可以为value指定多个值。

其他注释

基本和读取时一致:

  • @ContentRowHeight标注在类上或属性上,指定内容行高
  • @HeadRowHeight标注在类上或属性上,指定列头行高
  • @ColumnWidth标注在类上或属性上,指定列宽
  • @ExcelIgnore默认所有字段都会写入excel,这个注解会忽略这个字段
  • @DateTimeFormat日期转换,将Date写到excel会调用这个注释,里面的value参照java.text.SimpleDateFormat。
  • NumberFormat数字转换,用Number写excel会调用这个注解,里面的value参照java.text.DecimalFormat。
  • ExcelIgnoreUnannotated默认不加ExcelProperty的注解都会参与读写,加了不会参与。

写入时通用参数

writeworkbook、writesheet都会有的参数,如果为空,默认使用上级。

  • converter转换器,默认加载了很多转换器,也可以自定义。
  • writeHandler写的处理器。可以实现workbookwriteHandler,SheetWriteHandler,RowWriteHandler,CellWriteHandler,在写入excel的不同阶段会调用,对使用者透明不可见。
  • relativeHeadRowIndex距离多少行后开始。也就是开头空几行
  • needHead是否导出头
  • head与clazz二选一。写入文件的头列表,建议使用class。
  • clazz与head二选一。写入文件的头对应的class,也可以使用注解。
  • autoTrim字符串、表头等数据自动trim

WriteWorkBook(工作簿对象)参数

  • excelType当前excel的类型,默认为xlsx
  • outputStream与file二选一,写入文件的流
  • file与outputStream二选一,写入的文件
  • templateInputStream模板的文件流
  • templateFile模板文件
  • autoCloseStream自动关闭流
  • password写的时候是否需要使用密码
  • useDefaultStyle写的时候是否是使用默认头

WriteSheet(工作表对象)参数

  • sheetNo需要写入的编号,默认0
  • sheetName需要写的Sheet名称,默认同sheetNo

文件上传和下载

在这里插入图片描述

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.qrxqrx</groupId>
    <artifactId>easyexcel</artifactId>
    <version>1.0-SNAPSHOT</version>

    <packaging>war</packaging>

    <dependencies>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.8</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.25</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.0.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>javax.servlet-api</artifactId>
            <version>4.0.1</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>


</project>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
         version="4.0">

    <!-- 配置spring的监听器,默认只加载WEB-INF目录下的applicationContext.xml配置文件 -->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
    <!-- 设置配置文件的路径 -->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:beans.xml</param-value>
    </context-param>

    <!-- 解决中文乱码的过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</filter-name>
        <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
        <init-param>
            <param-name>encoding</param-name>
            <param-value>UTF-8</param-value>
        </init-param>
    </filter>
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

    <!-- 配置前端控制器 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 加载springmvc.xml配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <!-- 启动服务器,创建该servlet -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

</web-app>

spring-mvc.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.qrxqrx.easyexcel.web.controller"/>

    <bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver"/>

</beans>

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="com.qrxqrx.easyexcel">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

</beans>

log4j.properties

### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n


### direct message to file mylog.log ###
log4j.appender.file=org.apache.log4j.FileAppender
log4j.appender.file.File=d:/mylog.log
log4j.appender.file.layout=org.apache.log4j.PatternLayout
log4j.appender.file.layout.ConversionPattern=%d{ABSOLUTE} %5p %c{1}:%L - %m%n

### set log levels - for more verbose logging change 'info' to 'debug' ###

log4j.rootLogger=debug, stdout

Student

package com.qrxqrx.easyexcel.domain;

import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.write.style.ColumnWidth;
import com.alibaba.excel.annotation.write.style.ContentRowHeight;
import lombok.Data;

import java.util.Date;

@Data
// @ContentRowHeight() // 内容行高
public class Student {

    @ColumnWidth(20)
    @ExcelProperty(value = "学生姓名", index = 1)
    private String name;

    @ExcelProperty(value = "学生性别", index = 3)
    private String gender;

    @ColumnWidth(20)
    @ExcelProperty(value = "学生生日", index = 2)
    private Date birthday;

    @ExcelProperty(value = "ID", index = 4)
    private String id;

}

StudentService

package com.qrxqrx.easyexcel.service;

import com.qrxqrx.easyexcel.domain.Student;

import java.util.List;

public interface StudentService {

    void readExcel(List<Student> students);

}

StudentServiceImpl

package com.qrxqrx.easyexcel.service.impl;

import com.qrxqrx.easyexcel.domain.Student;
import com.qrxqrx.easyexcel.service.StudentService;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
public class StudentServiceImpl implements StudentService {

    @Override
    public void readExcel(List<Student> students) {
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

WebStudentListener

package com.qrxqrx.easyexcel.web.listener;


import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.qrxqrx.easyexcel.domain.Student;
import com.qrxqrx.easyexcel.service.StudentService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

import java.util.ArrayList;

@Component
@Scope("prototype")
public class WebStudentListener extends AnalysisEventListener<Student> {

    @Autowired
    StudentService studentService;

    ArrayList<Student> students = new ArrayList<>();

    @Override
    public void invoke(Student student, AnalysisContext analysisContext) {
        students.add(student);
        if (students.size() % 5 == 0) {
            studentService.readExcel(students);
            students.clear();
        }

    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

StudentController

package com.qrxqrx.easyexcel.web.controller;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.qrxqrx.easyexcel.domain.Student;
import com.qrxqrx.easyexcel.service.StudentService;
import com.qrxqrx.easyexcel.web.listener.WebStudentListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.Date;

@Controller
@RequestMapping("student")
public class StudentController {

    @Autowired
    WebStudentListener webStudentListener;

    // 上传
    @RequestMapping("read")
    @ResponseBody
    public String readExcel(MultipartFile uploadExcel) {
        try {
            // 工作簿
            ExcelReaderBuilder readWorkBook = EasyExcel.read(uploadExcel.getInputStream(), Student.class, webStudentListener);
            // 工作表
            readWorkBook.sheet().doRead();
            return "success";
        } catch (IOException e) {
            e.printStackTrace();
            return "fail";
        }
    }

    // 下载
    @RequestMapping("write")
    @ResponseBody
    public void writeExcel(HttpServletResponse httpServletResponse) throws IOException {
        httpServletResponse.setContentType("application/vnd.ms-excel");
        httpServletResponse.setCharacterEncoding("utf-8");
        String filename = URLEncoder.encode("data2", "UTF-8");
        httpServletResponse.setHeader("Content-Disposition","attachment; filename*=UTF-8''"+filename+".xlsx");
        ServletOutputStream outputStream = httpServletResponse.getOutputStream();
        ExcelWriterBuilder writeWorkBook = EasyExcel.write(outputStream, Student.class);
        ExcelWriterSheetBuilder sheet = writeWorkBook.sheet();
        ArrayList<Student> list = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Student student = new Student();
            student.setName("qrx"+i);
            student.setGender("男");
            student.setBirthday(new Date());
            list.add(student);
        }

        sheet.doWrite(list);

    }

}

在这里插入图片描述
在这里插入图片描述

填充

填充一组数据

准备模板

excel表格中用{}来包裹要填充的变量,如果单元格文本中本来就有{、}左右大括号,需要在括号前面使用斜杠转义\{、\}。

代码中被填充数据的实体对象的成员变量名或被填充map集合的key需要和excel中被{}包裹的变量名称一致。
在这里插入图片描述
FillData

package com.qrxqrx.easyexcel.domain;

import lombok.Data;

@Data
public class FillData {

    private String name;
    private int age;

}

ExcelTest

    @Test
    public void test03() {
        String template = "fill-data-template01.xlsx";
        ExcelWriterBuilder writeWorkBook = EasyExcel.write("data-fill01.xlsx", FillData.class).withTemplate(template);
        ExcelWriterSheetBuilder sheet = writeWorkBook.sheet();
        // 准备数据
        FillData fillData = new FillData();
        fillData.setName("qrx");
        fillData.setAge(25);

        // 准备map数据

        HashMap<String, String> map = new HashMap<>();
        map.put("name","qrxqrx");
        map.put("age","19");
        // sheet.doFill(fillData);
        sheet.doFill(map);
    }

填充多组数据

在这里插入图片描述

    @Test
    public void test04() {
        String template = "fill-data-template02.xlsx";
        ExcelWriterBuilder writeWorkBook = EasyExcel.write("data-fill02.xlsx", FillData.class).withTemplate(template);
        ExcelWriterSheetBuilder sheet = writeWorkBook.sheet();
        // 准备数据
        List<FillData> list = new ArrayList<>();
        for (int i = 0;i < 9;i++) {
            FillData fillData = new FillData();
            fillData.setName("abc"+i);
            fillData.setAge(i+10);
            list.add(fillData);
        }

        sheet.doFill(list);
    }

组合填充

在这里插入图片描述

    @Test
    public void test05() {
        String template = "fill-data-template03.xlsx";
        ExcelWriter build = EasyExcel.write("data-fill03.xlsx", FillData.class).withTemplate(template).build();
        WriteSheet sheet = EasyExcel.writerSheet().build();

        // 组合数据填充,因为多组填充的数据量不确定,需要在多组填充完之后另起一行
        FillConfig config = FillConfig.builder().forceNewRow(true).build();
        // 准备数据
        List<FillData> list = new ArrayList<>();
        for (int i = 0;i < 9;i++) {
            FillData fillData = new FillData();
            fillData.setName("abc"+i);
            fillData.setAge(i+10);
            list.add(fillData);
        }

        HashMap<String, String> map = new HashMap<>();
        map.put("date",new Date().toString());
        map.put("total","1w");

        build.fill(list, config, sheet);
        build.fill(map, sheet);

        build.finish();
    }

在这里插入图片描述

水平填充

在这里插入图片描述

    @Test
    public void test06() {
        String template = "fill-data-template04.xlsx";
        ExcelWriter build = EasyExcel.write("data-fill04.xlsx", FillData.class).withTemplate(template).build();
        WriteSheet sheet = EasyExcel.writerSheet().build();

        FillConfig config = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();
        // 准备数据
        List<FillData> list = new ArrayList<>();
        for (int i = 0;i < 9;i++) {
            FillData fillData = new FillData();
            fillData.setName("abc"+i);
            fillData.setAge(i+10);
            list.add(fillData);
        }


        build.fill(list, config, sheet);

        build.finish();
    }

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值