一 简介
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 最简单的读
调用EasyExcel
的API
读取的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
指定需要读表格的 列头行数。默认有一行头,也就是认为第二行开始起为数据。 -
head
与clazz
二选一。读取文件头对应的列表,会根据列表匹配数据。建议使用clas,就是文件中每一行数据对应的代码中的实体类型。 -
clazz
与head
二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。 -
autoTrim
字符串、表头等数据自动trim -
password
读的时候是否需要使用密码
4 ReadWorkbook(工作簿对象)参数
-
excelType
当前excel的类型,读取时会自动判断,无需设置。 -
inputStream
与file
二选一。建议使用file。 -
file
与inputStream
二选一。读取文件的文件。 -
autoCloseStream
自动关闭流。 -
readCache
默认小于5M用 内存,超过5M会使用EhCache
,不建议使用这个参数。 -
useDefaultListener
@since 2.1.4
默认会加入ModelBuildEventListener
来帮忙转换成传入class
的对象,设置成false
后将不会协助转换对象,自定义的监听器会接收到Map<Integer,CellData>
对象,如果还想继续接听到class
对象,请调用readListener
方法,加入自定义的beforeListener
、ModelBuildEventListener
、 自定义的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文件
五 数据填充
由于我们用代码设置填充数据是很复杂的,所以我们一般会准备一些模板,来代码按照模板的格式来进行填充。
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();