com.alibaba
easyexcel
3.0.5
姓名 |
年龄 |
创建时间 |
张三 |
20 |
2021-05-05 07:05:55 |
李四 |
15 |
2019-10-05 10:17:42 |
王五 |
22 |
2022-11-15 16:20:23 |
@Getter
@Setter
@EqualsAndHashCode
public class ExcelData {
/**
* 强制读取第0个 这里不建议 index 和 name 同时用,要么一个对象只用index,要么一个对象只用name去匹配
*/
@ExcelProperty(index = 0)
private String name;
/**
* 用名字去匹配,这里需要注意,如果名字重复,会导致只有一个字段读取到数据
*/
@ExcelProperty("年龄")
private Integer age;
@ExcelProperty("创建时间")
private Date date;
}
// 有个很重要的点 DemoDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
@Slf4j
public class DemoDataListener implements ReadListener {
/**
* 每隔5条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
*/
private static final int BATCH_COUNT = 100;
/**
* 缓存的数据
*/
private List cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
/**
* 假设这个是一个DAO,当然有业务逻辑这个也可以是一个service。当然如果不用存储这个对象没用。
*/
private DemoDAO demoDAO;
public DemoDataListener() {
// 这里是demo,所以随便new一个。实际使用如果到了spring,请使用下面的有参构造函数
demoDAO = new DemoDAO();
}
/**
* 如果使用了spring,请使用这个构造方法。每次创建Listener的时候需要把spring管理的类传进来
*
* @param demoDAO
*/
public DemoDataListener(DemoDAO demoDAO) {
this.demoDAO = demoDAO;
}
/**
* 这个每一条数据解析都会来调用
*
* @param data one row value. Is is same as {@link AnalysisContext#readRowHolder()}
* @param context
*/
@Override
public void invoke(DemoData data, AnalysisContext context) {
log.info("解析到一条数据:{}", JSON.toJSONString(data));
cachedDataList.add(data);
// 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
if (cachedDataList.size() >= BATCH_COUNT) {
saveData();
// 存储完成清理 list
cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
}
}
/**
* 所有数据解析完成了 都会来调用
*
* @param context
*/
@Override
public void doAfterAllAnalysed(AnalysisContext context) {
// 这里也要保存数据,确保最后遗留的数据也存储到数据库
saveData();
log.info("所有数据解析完成!");
}
/**
* 加上存储数据库
*/
private void saveData() {
log.info("{}条数据,开始存储数据库!", cachedDataList.size());
demoDAO.save(cache
dDataList);
log.info("存储数据库成功!");
}
}
package com.itheima.demo;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.read.builder.ExcelReaderSheetBuilder;
import com.itheima.domain.Student;
import com.itheima.listener.StudentReadListener;
import java.io.FileNotFoundException;
/**
* @Author Vsunks.v
* @Date 2020/3/11 23:28
* @Description:
*/
public class StudentReadDemo {
public static void main(String[] args) throws FileNotFoundException {
// 读取文件,读取完之后会自动关闭
/*
pathName 文件路径;"d:\\信息.xls"
head 每行数据对应的实体;Student.class
readListener 读监听器,每读一样就会调用一次该监听器的invoke方法
sheet方法参数: 工作表的顺序号(从0开始)或者工作表的名字,不传默认为0
*/
// 封装工作簿对象
ExcelReaderBuilder workBook = EasyExcel.read
("d:\\信息.xls", Student.class, new DemoDataListener());
// 封装工作表
ExcelReaderSheetBuilder sheet1 = workBook.sheet();
// 读取
sheet1.doRead();
}
}
/**
* 最简单的写
*
* 1. 创建excel对应的实体对象 参照{@link ExcelData}
*
* 2. 直接写即可
*/
@Test
public void simpleWrite() {
// 注意 simpleWrite在数据量不大的情况下可以使用(5000以内,具体也要看实际情况),数据量大参照 重复多次写入
// 写法1 JDK8+
// since: 3.0.0-beta1
String fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想
03 则 传入excelType参数即可
EasyExcel.write(fileName, ExcelData.class)
.sheet("模板")
.doWrite(() -> {
// 分页查询数据
return data();
});
// 写法2
fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
// 如果这里想使用03 则 传入excelType参数即可
EasyExcel.write(fileName, ExcelData.class).sheet("模板").doWrite(data());
// 写法3
fileName = TestFileUtil.getPath() + "simpleWrite" + System.currentTimeMillis() + ".xlsx";
// 这里 需要指定写用哪个class去写
ExcelWriter excelWriter = null;
try {
excelWriter = EasyExcel.write(fileName, ExcelData.class).build();
WriteSheet writeSheet = EasyExcel.writerSheet("模板").build();
excelWriter.write(data(), writeSheet);
} finally {
// 千万别忘记finish 会帮忙关闭流
if (excelWriter != null) {
excelWriter.finish();
}
}
}
@Controller
public class WebUploadAndDownload {
/**
\* ⽂件上传
\* 1. 编写excel中每⼀⾏对应的实体类
\* 2. 由于默认异步读取excel,所以需要逐⾏读取的回调监听器
\* 3. 开始读取Excel
*/
@PostMapping("upload")
@ResponseBody
public String upload(MultipartFile file)throws IOException {
//获取文件流进行上传
ExcelReaderBuilder workBook = EasyExcel.read(file.getInputStream(), Student.class, studentReadListener);
workBook.sheet().doRead();
return"success";
}
编写实体类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student {
/**
* id
*/
@ExcelIgnore
private String id;
/**
* 学⽣姓名
*/
//@ExcelProperty({"学员信息表", "学⽣姓名"})
@ExcelProperty("学⽣姓名")
private String name;
/**
* 学⽣性别
*/
//@ExcelProperty({"学员信息表", "学⽣性别"})
@ExcelProperty("学⽣性别")
private String gender;
/**
* 学⽣出⽣⽇期
*/
//@ExcelProperty({"学员信息表", "学⽣出⽣⽇期"}) @ExcelProperty("学⽣出⽣⽇期")
private Date birthday;
}
// 循环⽣成10个学⽣对象
private static List initData(){
ArrayList 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;
}
下载编码
public class WebUploadAndDownload {
/**
* ⽂件下载
* 1. 编写实体类并创建对象以便写⼊表格
* 2. 设置响应参数:⽂件的ContentType和⽂件名,同时设置编码避免乱码
* 3. 直接写,内部会调⽤finish⽅法⾃动关闭OutputStream
*/
@GetMapping("download")
public void download(HttpServletResponse response)throws IOException {
response.setContentType("application/vnd.ms-excel");
response.setCharacterEncoding("utf-8");
// 防⽌中⽂乱码
String fileName = URLEncoder.encode("测试","UTF-8");
response.setHeader("Content-Disposition","attachment; filename*=UTF-8''"+ fileName +".xlsx");
ExcelWriterBuilder workBook = EasyExcel.write(response.getOutputStream(), Student.class);
ExcelWriterSheetBuilder sheet = workBook.sheet("模板");
sheet.doWrite(initData());
}
}
关于常见类解析
读
注解
指定当前字段对应excel中的那一列。可以根据名字或者Index去匹配。当然也可以不写,默认第一个字段就是index=0,以此类推。千万注意,要么全部不写,要么全部用index,要么全部用名字去匹配。千万别三个混着用,除非你非常了解源代码中三个混着用怎么去排序的。
默认所有字段都会和excel去匹配,加了这个注解会忽略该字段
日期转换,用String去接收excel日期格式的数据会调用这个注解。里面的value参照java.text.SimpleDateFormat
数字转换,用String去接收excel数字格式的数据会调用这个注解。里面的value参照java.text.DecimalFormat
默认不加ExcelProperty 的注解的都会参与读写,加了不会参与
参数
通用参数
ReadWorkbook,ReadSheet 都会有的参数,如果为空,默认使用上级。
转换器,默认加载了很多转换器。也可以自定义。
监听器,在读取数据的过程中会不断的调用监听器。
需要读的表格有几行头数据。默认有一行头,也就是认为第二行开始起为数据。
与clazz二选一。读取文件头对应的列表,会根据列表匹配数据,建议使用class。
与head二选一。读取文件的头对应的class,也可以使用注解。如果两个都不指定,则会读取全部数据。
字符串、表头等数据自动trim
读的时候是否需要使用密码
6.2.2.2ReadWorkbook(理解成excel对象)参数
当前excel的类型 默认会自动判断
与file二选一。读取文件的流,如果接收到的是流就只用,不用流建议使用file参数。因为使用了inputStream easyexcel会帮忙创建临时文件,最终还是file
与inputStream二选一。读取文件的文件。
自动关闭流。
默认小于5M用 内存,超过5M会使用 EhCache,这里不建议使用这个参数。
@since 2.1.4 默认会加入ModelBuildEventListener 来帮忙转换成传入class的对象,设置成false后将不会协助转换对象,自定义的监听器会接收到Map对象,如果还想继续接听到class对象,请调用readListener方法,加入自定义的beforeListener、 ModelBuildEventListener、 自定义的afterListener即可。
ReadSheet(就是excel的一个Sheet)参数
需要读取Sheet的编码,建议使用这个来指定读取哪个Sheet
根据名字去匹配Sheet,excel 2003不支持根据名字去匹配
写
注解
index 指定写到第几列,默认根据成员变量排序。value指定写入的名称,默认成员变量的名字,多个value可以参照快速开始中的复杂头
默认所有字段都会写入excel,这个注解会忽略这个字段
日期转换,将Date写到excel会调用这个注解。里面的value参照java.text.SimpleDateFormat
数字转换,用Number写excel会调用这个注解。里面的value参照java.text.DecimalFormat
默认不加ExcelProperty 的注解的都会参与读写,加了不会参与
参数
通用参数
WriteWorkbook,WriteSheet ,WriteTable都会有的参数,如果为空,默认使用上级。
转换器,默认加载了很多转换器。也可以自定义。
写的处理器。可以实现WorkbookWriteHandler,SheetWriteHandler,RowWriteHandler,CellWriteHandler,在写入excel的不同阶段会调用
距离多少行后开始。也就是开头空几行
是否导出头
与clazz二选一。写入文件的头列表,建议使用class。
与head二选一。写入文件的头对应的class,也可以使用注解。
字符串、表头等数据自动trim
WriteWorkbook(理解成excel对象)参数
当前excel的类型 默认xlsx
与file二选一。写入文件的流
与outputStream二选一。写入的文件
模板的文件流
模板文件
自动关闭流。
写的时候是否需要使用密码
写的时候是否是使用默认头
6.3.2.3WriteSheet(就是excel的一个Sheet)参数
需要写入的编码。默认0
需要些的Sheet名称,默认同sheetNo
WriteTable(就把excel的一个Sheet,一块区域看一个table)参数
需要写入的编码。默认0