EasyExcel

一 简介

1.EasyExcel是什么

EasyExcel是一个基于Java的简单、省内存的读写Excel的阿里开源项目在尽可能节约内存的情况下支持读写百M的Excel。

2.EasyExcel 能用在哪里

项目中涉及到Excel文件,CVS文件大多数的读写操作,均可以使用!

3 官网

EasyExcel官方文档 - 基于Java的Excel处理工具 | Easy Excel 官网

二 快速入门

1 前置案例准备

我们有一个表格

与之配套的实体类,这里只是一个简单的对应关系

java 复制代码
package domain;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.Date;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {


    /**
     * 学生姓名
     */
    private String name;
    /**
     * 学生出生日期
     */
    private Date birthday;
    /**
     * 学生性别
     */
    private String gender;
    /**
     * id
     */
    private String id;



}

2 引入依赖

引入easyexcel的依赖

java 复制代码
 <!-- EasyExcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
        </dependency>

3 最简单的读

调用EasyExcelAPI读取的Excel文件的测试类StudentReadDemo

java 复制代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderBuilder;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import domain.Student;
import listener.StudentReadListener;

public class StudentReadDemo {

    public static void main(String[] args) {

        // 读取文件,读取完之后会自动关闭
        /*
        	pathName  		文件路径;"d:\\杭州黑马在线202003班学员信息.xls"
        	head			每行数据对应的实体;Student.class
        	readListener	读监听器,每读一样就会调用一次该监听器的invoke方法

        	sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字,不传默认为0
        */
        //创建一个工作簿对象
        ExcelReaderBuilder read = EasyExcel.read(
                "E:\\java\\itheima\\EasyExcel" +
                "公开课资料\\杭州黑马在线202003班学员信息表.xlsx", Student.class, new StudentReadListener());


        //创建一个工作表对象
        ExcelReaderSheetBuilder sheet = read.sheet();

        //读取信息
        sheet.doRead();
    }
}

读取Excel的监听器,用于处理读取产生的数据

java 复制代码
package listener;

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

public class StudentReadListener extends AnalysisEventListener<Student> {


    /*
    每次读取到一行数据都会调用一次invoke方法,在这个方法内可以操作读取到的数据
     */
    @Override
    public void invoke(Student student, AnalysisContext analysisContext) {
        System.out.println(student);
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {

    }
}

测试

4 最简单的写

编写导出数据的实体,修改原来的student实体类

@ColumnWidth(20):每一个字段宽度都设置成20个字符

@HeadRowHeight(15):表头的行高是15个字符

@ContentRowHeight(10):内容的行高是10个字符

@ExcelIgnore:生成的表中不会显示id

@ExcelProperty(value = "学生姓名", index = 0):规定表中第一行的数据的名字和位置

java 复制代码
@Data
@AllArgsConstructor
@NoArgsConstructor
@ColumnWidth(20)
@HeadRowHeight(15)
@ContentRowHeight(10)
public class Student {


    /**
     * id
     */
    //@ExcelProperty(value = "编号",index = 3)
    @ExcelIgnore
    private String id;
    /**
     * 学生姓名
     */
    @ExcelProperty(value = "学生姓名", index = 0)
    //@ColumnWidth(30)
    private String name;
    /**
     * 学生性别
     */
    @ExcelProperty(value = "学生性别", index = 2)
    private String gender;

    /**
     * 学生出生日期
     */
    @ExcelProperty(value = "学生出生日期", index = 1)
    //@ColumnWidth(20)
    private Date birthday;
}

简单的写操作:

定义了一个生成学生实体类的方法,获取学生实体类然后调用写方法。

java 复制代码
@Test
    public void write(){

        List<Student> students = initData();
        /*
            String pathName 写入文件的路径
            Class head      写入文件的对象类型
            默认写入到07的xlsx中,如果想要写入xls,可以指定类型(待验证)
         */
        ExcelWriterBuilder workBook = EasyExcel.write("杭州黑马学员表写入.xlsx", Student.class);

        // sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字
        workBook.sheet().doWrite(students);
    }

    private List<Student> initData() {
        ArrayList<Student> students = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Student data = new Student();
            data.setName("杭州黑马学号0" + i);
            data.setBirthday(new Date());
            data.setGender("男");
            students.add(data);
        }
        return students;
    }

最后生成的表:

三 常用的api

1、常用类

  • EasyExcel 入口类,用于构建开始各种操作;

  • ExcelReaderBuilder 构建出一个ReadWorkbook对象,即一个工作簿对象,对应的是一个Excel文件;

  • ExcelWriterBuilder 构建出一个WriteWorkbook对象,即一个工作簿对象,对应的是一个Excel文件;

  • ExcelReaderSheetBuilder 构建出一个ReadSheet对象,即一个工作表的对象,对应的Excel中的每个sheet,一个工作簿可以有多个工作表;

  • ExcelWriterSheetBuilder 构建出一WriteSheet对象,即一个工作表的对象,对应的Excel中的每个sheet,一个工作簿可以有多个工作表;

  • ReadListener 在每一行读取完毕后都会调用ReadListener来处理数据,我们可以把调用service的代码可以写在其invoke方法内部;

  • WriteHandler 在每一个操作包括创建单元格、创建表格等都会调用WriteHandler来处理数据,对使用者透明不可见;

  • 所有配置都是继承的 Workbook的配置会被Sheet继承。所以在用EasyExcel设置参数的时候,在EasyExcel...sheet()方法之前作用域是整个sheet,之后针对单个sheet。

2、读取时的注解

@ExcelProperty

使用位置:标准作用在成员变量上

可选属性:

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

使用效果:index属性可以指定当前字段对应excel中的哪一列,可以根据列名value去匹配,也可以不写。

如果不使用@ExcelProperty注解,成员变量从上到下的顺序,对应表格中从左到右的顺序;

**使用建议:**要么全部不写,要么全部用index,要么全部用名字去匹配,尽量不要三个混着用。

@ExcelIgnore

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

@DateTimeFormat

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

@NumberFormat

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

示例代码:

将学生类里面的birthday由原来的Date转变为String,加上注解

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {


    /**
     * 学生姓名
     */
    private String name;
    /**
     * 学生出生日期
     */
    @DateTimeFormat
//    private Date birthday;
    private String birthday;
    /**
     * 学生性别
     */
    private String gender;
    /**
     * id
     */
    private String id;

}

读取数据代码:

java 复制代码
@Test
    public void read(){
        // 读取文件,读取完之后会自动关闭
        /*
        	pathName  		文件路径;"d:\\杭州黑马在线202003班学员信息.xls"
        	head			每行数据对应的实体;Student.class
        	readListener	读监听器,每读一样就会调用一次该监听器的invoke方法

        	sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字,不传默认为0
        */
        //1  创建一个工作簿对象
        ExcelReaderBuilder read = EasyExcel.read(
                "E:\\java\\itheima\\EasyExcel" +
                        "公开课资料\\杭州黑马在线202003班学员信息表.xlsx", Student.class, new StudentReadListener());


        //2  创建一个工作表对象
        ExcelReaderSheetBuilder sheet = read.sheet();

        //3  读取信息
        sheet.doRead();

    }

成功读取:

@ExcelIgnoreUnannotated

标注在类上。

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

标注该注解后,类中的成员变量如果没有标注@ExcelProperty 注解将不会参与读写。

示例代码:

student类加上@ExcelIgnoreUnannotated注解,有gender,name加上了 @ExcelProperty注解

java 复制代码
@Data
@ColumnWidth(20)
@HeadRowHeight(30)
@ContentRowHeight(20)
@ExcelIgnoreUnannotated
public class Student {


    /**
     * id
     */
//    @ExcelProperty(value = "编号",index = 3)
    @ExcelIgnore
    private String id;
    /**
     * 学生姓名
     */
    @ExcelProperty(value = "学生姓名", index = 0)
    //@ColumnWidth(30)
    private String name;
    /**
     * 学生性别
     */
    @ExcelProperty(value = "学生性别", index = 2)
    private String gender;

    /**
     * 学生出生日期
     */
//    @ExcelProperty(value = "学生出生日期", index = 1)
    //@ColumnWidth(20)
    private Date birthday;
}

写入数据代码:

java 复制代码
​
    @Test
    public void write(){

        List<Student> students = initData();
        /*
            String pathName 写入文件的路径
            Class head      写入文件的对象类型
            默认写入到07的xlsx中,如果想要写入xls,可以指定类型(待验证)
         */
        ExcelWriterBuilder workBook = EasyExcel.write("杭州黑马学员表写入.xlsx", Student.class);

        // sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字
        workBook.sheet().doWrite(students);
    }

    private List<Student> initData() {
        ArrayList<Student> students = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Student data = new Student();
            data.setName("杭州黑马学号0" + i);
            data.setBirthday(new Date());
            data.setGender("男");
            students.add(data);
        }
        return students;
    }

​

结果,与预期一致。

3 读取时通用参数

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

  • converter 转换器,默认加载了很多转换器。也可以自定义。

  • readListener 监听器,在读取数据的过程中会不断的调用监听器。

  • headRowNumber 指定需要读表格的 列头行数。默认有一行头,也就是认为第二行开始起为数据。

  • headclazz二选一。读取文件头对应的列表,会根据列表匹配数据。建议使用clas,就是文件中每一行数据对应的代码中的实体类型。

  • clazzhead二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。

  • autoTrim 字符串、表头等数据自动trim

  • password 读的时候是否需要使用密码

4 ReadWorkbook(工作簿对象)参数

  • excelType 当前excel的类型,读取时会自动判断,无需设置。

  • inputStreamfile二选一。建议使用file。

  • fileinputStream二选一。读取文件的文件。

  • autoCloseStream 自动关闭流。

  • readCache 默认小于5M用 内存,超过5M会使用 EhCache,不建议使用这个参数。

  • useDefaultListener @since 2.1.4 默认会加入ModelBuildEventListener 来帮忙转换成传入class的对象,设置成false后将不会协助转换对象,自定义的监听器会接收到Map<Integer,CellData>对象,如果还想继续接听到class对象,请调用readListener方法,加入自定义的beforeListenerModelBuildEventListener、 自定义的afterListener即可。

5 ReadSheet(工作表对象)参数

  • sheetNo 需要读取Sheet的编号,建议使用这个来指定读取哪个Sheet。如果不指定参数默认读取第一张表

  • sheetName 根据名字去匹配Sheet,excel 2003不支持根据名字去匹配

6、写入时的注解注解

@ExcelProperty

使用位置:标准作用在成员变量上

可选属性:

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

使用效果index 指定写到第几列,如果不指定则根据成员变量位置排序;

value指定写入的列头,如果不指定则使用成员变量的名字作为列头;

如果要设置复杂的头,可以为value指定多个值。

示例代码1:

给名字设置了两个表头

java 复制代码
@Data
@ColumnWidth(20)
@HeadRowHeight(30)
@ContentRowHeight(20)
//@ExcelIgnoreUnannotated
public class Student {


    /**
     * id
     */
//    @ExcelProperty(value = "编号",index = 3)
    @ExcelIgnore
    private String id;
    /**
     * 学生姓名
     */
    @ExcelProperty(value = {"student_name", "学生姓名"}, index = 0)
    //@ColumnWidth(30)
    private String name;
    /**
     * 学生性别
     */
    @ExcelProperty(value = "学生性别", index = 2)
    private String gender;

    /**
     * 学生出生日期
     */
    @ExcelProperty(value = "学生出生日期", index = 1)
    //@ColumnWidth(20)
    private Date birthday;
}

写入代码:

java 复制代码
 @Test
    public void write(){

        List<Student> students = initData();
        /*
            String pathName 写入文件的路径
            Class head      写入文件的对象类型
            默认写入到07的xlsx中,如果想要写入xls,可以指定类型(待验证)
         */
        ExcelWriterBuilder workBook = EasyExcel.write("杭州黑马学员表写入.xlsx", Student.class);

        // sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字
        workBook.sheet().doWrite(students);
    }

    private List<Student> initData() {
        ArrayList<Student> students = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Student data = new Student();
            data.setName("杭州黑马学号0" + i);
            data.setBirthday(new Date());
            data.setGender("男");
            students.add(data);
        }
        return students;
    }

结果:

示例代码2:

给所有字段都加上表头"学生表",写入代码与上面一致

java 复制代码
@Data
@ColumnWidth(20)
@HeadRowHeight(30)
@ContentRowHeight(20)
//@ExcelIgnoreUnannotated
public class Student {


    /**
     * id
     */
//    @ExcelProperty(value = "编号",index = 3)
    @ExcelIgnore
    private String id;
    /**
     * 学生姓名
     */
    @ExcelProperty(value = {"学生表", "学生姓名"}, index = 0)
    //@ColumnWidth(30)
    private String name;
    /**
     * 学生性别
     */
    @ExcelProperty(value = {"学生表", "学生性别"}, index = 2)
    private String gender;

    /**
     * 学生出生日期
     */
    @ExcelProperty(value = {"学生表", "学生出生日期"}, index = 1)
    //@ColumnWidth(20)
    private Date birthday;
}

效果:

本来应该是有三个格子充当表头,现在合并成一个

其他注解:

基本和读取时一致

  • @ContentRowHeight() 标注在类上或属性上,指定内容行高

  • @HeadRowHeight() 标注在类上或属性上,指定列头行高

  • @ColumnWidth() 标注在类上或属性上,指定列宽

  • @ExcelIgnore` 默认所有字段都会写入excel,这个注解会忽略这个字段

  • @DateTimeFormat 日期转换,将Date写到excel会调用这个注解。里面的value参照java.text.SimpleDateFormat

  • @NumberFormat 数字转换,用Number写excel会调用这个注解。里面的value参照java.text.DecimalFormat

  • @ExcelIgnoreUnannotated 默认不加 ExcelProperty 的注解的都会参与读写,加了不会参与

四 文件上传和下载

1 模板准备

1.1 引入依赖

java 复制代码
<dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>


        <!-- EasyExcel -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>easyexcel</artifactId>
            <version>2.1.6</version>
        </dependency>
        <!-- lombok -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.10</version>
            <scope>provided</scope>
        </dependency>
        <!-- junit -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-nop</artifactId>
            <version>1.7.2</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-log4j2 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-log4j2</artifactId>
            <version>3.2.4</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>2.7.11</version>
        </dependency>

        <!-- 文件上传 -->
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>
    </dependencies>

1.2 准备要上传的表格

1.3 编写对应的实体类

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
//@ExcelIgnoreUnannotated
public class Student {


    /**
     * 学生姓名
     */
//    @ExcelProperty(value = "名字", index = 0)
    private String name;
    /**
     * 学生出生日期
     */
//    @DateTimeFormat
    private Date birthday;
//    private String birthday;
    /**
     * 学生性别
     */
    private String gender;
    /**
     * id
     */

    private String id;

}

2 文件上传(就是从外界传入文件进后端)

2.1 编写Conteoller

这时候传入file输入流给easyexcel读取文件里面的数据

java 复制代码
@Slf4j
@RestController
@RequestMapping("/file")
public class StudentController {

    @Resource
    private StudentReadListener studentReadListener;

    @PostMapping("/read")
    public void testRead(MultipartFile file){

        try {
            ExcelReaderBuilder read = EasyExcel.read(file.getInputStream()
                    , Student.class, studentReadListener);
            ExcelReaderSheetBuilder sheet = read.sheet();
            sheet.doRead();
            log.info("成功");
        } catch (Exception e) {
            log.error(String.valueOf(e));
        }
    }
}

2.2 service

输出学生信息

java 复制代码
@Service
@Slf4j
public class StudentServiceImpl implements StudentService {
    @Override
    public void save(ArrayList<Student> students) {
        for(Student s : students){
            log.info(String.valueOf(s));
        }
    }
}

2.3 自定义监听器

每读到五条记录就输出

java 复制代码
@Component
@Scope("prototype")	// 作者要求每次读取都要使用新的Listener
public class StudentReadListener extends AnalysisEventListener<Student> {

    @Resource
    private StudentService studentService;

    private final int BATCH_SAVE_NUM = 5;
    ArrayList<Student> students = new ArrayList<>();


    private int count = 0;

    // 每读一次,会调用该invoke方法一次
    @Override
    public void invoke(Student data, AnalysisContext context) {
        students.add(data);
        if (++count % BATCH_SAVE_NUM == 0) {
            studentService.save(students);
            students.clear();
        }
    }

    // 全部读完之后,会调用该方法
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // TODO......
    }

2.4 测试

使用apifox

上传文件请求,首先设置请求头内的参数和参数值

然后设置上传参数的类型名字,类型以及文件本身

测试结果:

3 文件下载(就是从后端读取数据生成excel)

简单的演示,直接将业务逻辑都写在了controller上。这里注入了一个http响应,给easyExcel的write方法

java 复制代码
@GetMapping("/write")
    public void testWrite(HttpServletResponse response) throws IOException {
        // 设置响应的内容类型为Excel文件格式
        response.setContentType("application/vnd.ms-excel");

        // 设置响应的字符编码为UTF-8,确保中文字符不会乱码
        response.setCharacterEncoding("utf-8");

        // 为了防止中文文件名在下载时出现乱码,使用URLEncoder对文件名进行编码
        // "UTF-8"指定了编码格式,确保文件名在不同语言环境下都能正确显示
        String fileName = URLEncoder.encode("测试", "UTF-8");

        // 设置响应头,告诉浏览器这是一个附件,并指定下载后的文件名
        // "attachment"告诉浏览器这是一个需要下载的文件
        // "filename*"指定了文件名的编码方式,"UTF-8''"表示使用UTF-8编码,并在文件名两边加上空格以避免浏览器解析错误
        // 将编码后的文件名和.xlsx后缀拼接,形成完整的文件名
        response.setHeader("Content-Disposition", "attachment; filename*=UTF-8''" + fileName + ".xlsx");
        /*
            String pathName 写入文件的路径
            Class head      写入文件的对象类型
            默认写入到07的xlsx中,如果想要写入xls,可以指定类型(待验证)
         */
        ExcelWriterBuilder workBook = EasyExcel.write(response.getOutputStream(), Student.class);


        List<Student> students = initData();
        // sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字
        workBook.sheet().doWrite(students);
    }


    private List<Student> initData() {
        ArrayList<Student> students = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            Student data = new Student();
            data.setName("杭州黑马学号0" + i);
            data.setBirthday(new Date());
            data.setGender("男");
            students.add(data);
        }
        return students;
    }

演示:

当我访问相应的路径的时候,网页自动下载excel文件

localhost:8080/file/write

五 数据填充

由于我们用代码设置填充数据是很复杂的,所以我们一般会准备一些模板,来代码按照模板的格式来进行填充。

1 填充一组数据

1.1 准备模板

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

代码中被填充数据的实体对象的成员变量名或被填充map集合的key需要和Excel中被{}包裹的变量名称一致。

1.2 封装数据

编写封装填充数据的类或选用Map

java 复制代码
/**
 * 使用实体类封装填充数据
 *
 *  实体中成员变量名称需要和Excel表各种{}包裹的变量名匹配
 */
@Data
public class FillData {

    private String name;
    private int age;
}

1.3 填充

这里只能使用自动关闭输出流的方法,因为手动关闭那个需要传入的数据是已list的方式

java 复制代码
@Test
    void testOne(){
        // 加载模板
        String template = "fill_data_template1.xlsx";

        // 准备工作簿
        ExcelWriterBuilder excelWriterBuilder
                = EasyExcel.write("填充-单个数据.xlsx", FillData.class).withTemplate(template);

        // 准备单个工作表
        ExcelWriterSheetBuilder sheet = excelWriterBuilder.sheet();

        // 准备数据填充
        //方法一:使用实体类
//        FillData fillData = new FillData();
//        fillData.setAge(100);
//        fillData.setName("吴彦煮");

        //方法2:使用map
        HashMap<String, String> fillData = new HashMap<>();
        fillData.put("name", "陈冠希");
        fillData.put("age", "50");

        // 填充数据
        sheet.doFill(fillData);
    }

1.4 测试

测试成功

2 填充多组数据

2.1 准备模板

注意,由于我们要填充多组数据,我们要在name,age前面加上一点 **'.',**代表多组数据

2.2 填充

方式1 自动关闭输出流
java 复制代码
@Test
    void testMoreThanOne(){
        // 加载模板
        String template = "fill_data_template2.xlsx";

        // 准备工作簿
        ExcelWriterBuilder excelWriterBuilder
                = EasyExcel.write("填充-多个数据.xlsx", FillData.class).withTemplate(template);

        // 准备单个工作表
        ExcelWriterSheetBuilder sheet = excelWriterBuilder.sheet();

        List<FillData> fillData = initFillData();

        // 填充数据
        sheet.doFill(fillData);
    }

    private static List<FillData> initFillData() {
        ArrayList<FillData> fillDatas = new ArrayList<FillData>();
        for (int i = 0; i < 10; i++) {
            FillData fillData = new FillData();
            fillData.setName("杭州黑马0" + i);
            fillData.setAge(10 + i);
            fillDatas.add(fillData);
        }
        return fillDatas;
    }
方式2 手动关闭输出流
java 复制代码
@Test
    void testMoreThanOne(){
        // 加载模板
        String template = "fill_data_template2.xlsx";

        // 准备工作簿
        ExcelWriter workBook = EasyExcel
                .write("填充-多个数据.xlsx", FillData.class)
                .withTemplate(template)
                .build();

        // 准备单个工作表
        WriteSheet writeSheet = EasyExcel.writerSheet().build();

        List<FillData> fillData = initFillData();

        // 填充数据
        workBook.fill(fillData, writeSheet);

        // 关闭流
        workBook.finish();
    }

    private static List<FillData> initFillData() {
        ArrayList<FillData> fillDatas = new ArrayList<FillData>();
        for (int i = 0; i < 10; i++) {
            FillData fillData = new FillData();
            fillData.setName("杭州黑马0" + i);
            fillData.setAge(10 + i);
            fillDatas.add(fillData);
        }
        return fillDatas;
    }

2.3 测试

成功

3 填充一组和多组数据(组合数据)

3.1 准备模板

3.2 填充

注意!

这里创建工作簿和工作表对象发生了变化 。因为我们需要分布去填充数据。上面两个例子使用的方法 dofill ,只要前缀是do 开头,都是默认自动关闭流的。我们写入,也就是填充操作是依靠输出流的。当完成一个填充操作后输出流自动关闭,就进行不了下一步的填充。所以我们不能使用上面两个用的dofill方法。我们需要自己手动去关闭输出流

另外,组合填充时,因为多组填充的数据量不确定,需要在多组填充完之后另开一行。如果不手动开一行,那么单行和多行就会混在一起。

java 复制代码
@Test
    void testMoreAndOne(){
        // 加载模板
        String template = "fill_data_template3.xlsx";

        // 准备工作簿
        ExcelWriter workBook = EasyExcel.write("填充-组合数据.xlsx", FillData.class)
                .withTemplate(template).build();

        // 准备单个工作表
        WriteSheet writeSheet = EasyExcel.writerSheet().build();

        // 准备数据填充
        List<FillData> fillDatas = initFillData();

        HashMap<String, String> fillDate = new HashMap<>();
        fillDate.put("date", String.valueOf(new Date()));
        fillDate.put("total", "10");

        // 组合填充时,因为多组填充的数据量不确定,需要在多组填充完之后另起一行
        FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();

        // 填充数据
        workBook.fill(fillDatas, fillConfig, writeSheet);
        workBook.fill(fillDate, writeSheet);

        // 手动关闭流
        workBook.finish();

    }

    private static List<FillData> initFillData() {
        ArrayList<FillData> fillDatas = new ArrayList<FillData>();
        for (int i = 0; i < 10; i++) {
            FillData fillData = new FillData();
            fillData.setName("杭州黑马0" + i);
            fillData.setAge(10 + i);
            fillDatas.add(fillData);
        }
        return fillDatas;
    }

3.3 测试

4 水平填充

4.1 准备模板

水平,顾名思义是横着来填写数据的。也是封装多组数据

4.2 填充

这里通过调整方向来填充。自己手动关闭流,因为要传入fillConfig对象

java 复制代码
@Test
    void HORIZONTAL(){
        // 加载模板
        String template = "fill_data_template4.xlsx";

        // 准备工作簿
        ExcelWriter workBook = EasyExcel.write("填充-水平数据.xlsx", FillData.class)
                .withTemplate(template).build();

        // 准备单个工作表
        WriteSheet writeSheet = EasyExcel.writerSheet().build();

        // 准备数据填充
        List<FillData> fillDatas = initFillData();

        // 这里设置一下填充方向,水平填充,通过WriteDirectionEnum来设置
        FillConfig fillConfig = FillConfig.builder().direction(WriteDirectionEnum.HORIZONTAL).build();

        // 填充数据
        workBook.fill(fillDatas, fillConfig, writeSheet);

        // 手动关闭流
        workBook.finish();

    }

    private static List<FillData> initFillData() {
        ArrayList<FillData> fillDatas = new ArrayList<FillData>();
        for (int i = 0; i < 10; i++) {
            FillData fillData = new FillData();
            fillData.setName("杭州黑马0" + i);
            fillData.setAge(10 + i);
            fillDatas.add(fillData);
        }
        return fillDatas;
    }

可以设置水平和垂直填充

4.3 测试

5 总结

我们填充数据有两种代码实现方式,精确的来说就是我们有两种不一样的创建工作簿和工作表的方法

方式1

这一种在填充数据的时候是调用工作表对象的填充方法,自动关闭输出流

java 复制代码
// 准备工作簿
        ExcelWriterBuilder excelWriterBuilder
                = EasyExcel.write("填充-单个数据.xlsx", FillData.class).withTemplate(template);

        // 准备单个工作表
        ExcelWriterSheetBuilder sheet = excelWriterBuilder.sheet();

方式2

这一种是在填充数据的时候调用工作簿对象的方法,需要手动关闭输出流。

java 复制代码
// 准备工作簿
        ExcelWriter workBook = EasyExcel.write("填充-水平数据.xlsx", FillData.class)
                .withTemplate(template).build();

        // 准备单个工作表
        WriteSheet writeSheet = EasyExcel.writerSheet().build();
相关推荐
星河梦瑾4 分钟前
SpringBoot相关漏洞学习资料
java·经验分享·spring boot·安全
黄名富8 分钟前
Redis 附加功能(二)— 自动过期、流水线与事务及Lua脚本
java·数据库·redis·lua
love静思冥想9 分钟前
JMeter 使用详解
java·jmeter
言、雲12 分钟前
从tryLock()源码来出发,解析Redisson的重试机制和看门狗机制
java·开发语言·数据库
TT哇19 分钟前
【数据结构练习题】链表与LinkedList
java·数据结构·链表
Yvemil71 小时前
《开启微服务之旅:Spring Boot 从入门到实践》(三)
java
Anna。。1 小时前
Java入门2-idea 第五章:IO流(java.io包中)
java·开发语言·intellij-idea
.生产的驴1 小时前
SpringBoot 对接第三方登录 手机号登录 手机号验证 微信小程序登录 结合Redis SaToken
java·spring boot·redis·后端·缓存·微信小程序·maven
爱上语文1 小时前
宠物管理系统:Dao层
java·开发语言·宠物
王ASC2 小时前
SpringMVC的URL组成,以及URI中对/斜杠的处理,解决IllegalStateException: Ambiguous mapping
java·mvc·springboot·web