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();
相关推荐
CodeAmaz2 分钟前
Spring编程式事务详解
java·数据库·spring
没有bug.的程序员4 分钟前
微服务基础设施清单:必须、应该、可以、无需的四级分类指南
java·jvm·微服务·云原生·容器·架构
武子康6 分钟前
Java-204 RabbitMQ Connection/Channel 工作流程:AMQP 发布消费、抓包帧结构与常见坑
java·分布式·消息队列·rabbitmq·ruby·java-activemq
郑州光合科技余经理8 分钟前
海外国际版同城服务系统开发:PHP技术栈
java·大数据·开发语言·前端·人工智能·架构·php
appearappear18 分钟前
Mac 上重新安装了Cursor 2.2.30,重新配置 springboot 过程记录
java·spring boot·后端
CryptoRzz27 分钟前
日本股票 API 对接实战指南(实时行情与 IPO 专题)
java·开发语言·python·区块链·maven
程序员水自流29 分钟前
MySQL数据库自带系统数据库功能介绍
java·数据库·mysql·oracle
谷哥的小弟34 分钟前
Spring Framework源码解析——RequestContext
java·后端·spring·框架·源码
天远Date Lab39 分钟前
Java微服务实战:聚合型“全能小微企业报告”接口的调用与数据清洗
java·大数据·python·微服务
lizz3144 分钟前
C++操作符重载深度解析
java·c++·算法