Java系列文章
文章目录
前言
本文将介绍如何使用SpringBoot整合EasyExcel,实现Excel导入及分批导入功能。
一、基础环境搭建
1.1 环境搭建
- SpringBoot 2.7 构建项目
- Maven依赖
- Apifox/Apipost/Postman
1.2 pom.xml
<!--easyexcel-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>4.0.0</version>
</dependency>
<!-- JUnit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
1.3 Excel文件数据
二、导入方式
2.1 基本方式导入
2.1.1 编写测试类
@Test
public void TestExcel1001() {
File file = new File("C:\\Users\\lenovo\\Desktop\\movie.xlsx");
List<Map<Integer, String>> list = EasyExcel.read(file).sheet(0).doReadSync();
for (Map<Integer, String> item : list) {
log.info("title:{},director:{},duration:{},rating:{},type:{},spider:{}", item.get(0), item.get(1), item.get(2), item.get(3), item.get(4), item.get(5));
}
}
2.1.1 导入数据展示
2.2 模型映射导入
2.2.1 映射实体类
@Data
public class MovieForm {
@ExcelProperty(value = "标题")
private String title;
@ExcelProperty(value = "作者")
private String director;
@ExcelProperty(value = "时长")
private String duration;
@ExcelProperty(value = "评分")
private String rating;
@ExcelProperty(value = "类型")
private String type;
@ExcelProperty(value = "来源",converter = MovieInfoGenderConverter.class)
private String spider;
@ExcelProperty(value = "日期")
@DateTimeFormat(value = "yyyy-MM-dd")
private String date;
@ExcelProperty(value = "票房")
@NumberFormat(value = "###,####")
private Integer grossed;
}
2.2.2 转换器实现类
提示:某些场景下,需要对导入的数据进行数据转换处理,如字段为男女,进行转移1和2
public class MovieInfoGenderConverter implements Converter<String> {
@Override
public String convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {
String value = cellData.getStringValue();
switch (value) {
case "douban":
return "1";
case "btdx":
return "2";
default:
return "0";
}
}
}
2.2.3 实体类注解
名称 | 描述 |
---|---|
ExcelProperty | 用于匹配excel和实体类的匹配。 |
ExcelIgnore | 默认所有字段都会和excel去匹配,加了这个注解会忽略该字段 。 |
DateTimeFormat | 日期转换,用String去接收excel日期格式的数据会调用这个注解 。 |
NumberFormat | 数字转换,用String去接收excel数字格式的数据会调用这个注解 。 |
2.2.4 编写测试类
/**
* 模型映射导入
*/
@Test
public void TestExcel1002() {
File file = new File("C:\\Users\\lenovo\\Desktop\\movie.xlsx");
List<MovieForm> list = EasyExcel.read(file).head(MovieForm.class).sheet(0).doReadSync();
for (MovieForm item : list) {
log.info("title:{},director:{},duration:{},rating:{},type:{},spider:{}", item.getTitle(), item.getDirector(), item.getDuration(), item.getRating(), item.getType(), item.getSpider());
}
}
2.3 文件上传导入
2.3.1 编写导入接口
@PostMapping("/easy/importExcel")
public R importExcel(@RequestParam(value = "file") MultipartFile file) {
try {
ArrayList<HashMap> movieList = new ArrayList<>();
HashMap<String, Object> map = new HashMap<>();
List<MovieForm> list = EasyExcel.read(file.getInputStream()).head(MovieForm.class).sheet(0).doReadSync();
for (MovieForm item : list) {
map.put("title",item.getTitle());
map.put("director",item.getDirector());
map.put("duration",item.getDuration());
map.put("rating",item.getRating());
map.put("type",item.getType());
map.put("spider",item.getSpider());
map.put("date",item.getDate());
map.put("grossed",item.getGrossed());
log.info("title:{},director:{},duration:{},rating:{},type:{},spider:{}", item.getTitle(), item.getDirector(), item.getDuration(), item.getRating(), item.getType(), item.getSpider());
movieList.add(map);
}
return R.success(movieList);
} catch (Exception e) {
throw new MisException(e);
}
}
2.3.2 apifox文件上传
2.4 多行表头导入
2.4.1 Excel表头描述
2.4.2 编写测试类
提示:从第六行开始导入
/**
* 多行表头导入
*/
@Test
public void TestExcel1003() {
File file = new File("C:\\Users\\lenovo\\Desktop\\movie2.xlsx");
List<MovieForm> list = EasyExcel.read(file).head(MovieForm.class).headRowNumber(6).sheet(0).doReadSync();
for (MovieForm item : list) {
log.info("title:{},director:{},duration:{},rating:{},type:{},spider:{}", item.getTitle(), item.getDirector(), item.getDuration(), item.getRating(), item.getType(), item.getSpider());
}
}
三、分批导入
3.1 编写测试类
/**
* 导入监听器
*/
@Test
public void TestReadListener() {
File file = new File("C:\\Users\\lenovo\\Desktop\\movie.xlsx");
// 条件判断
Predicate<MovieForm> predicate = (item) -> item.getSpider() == "1";
MovieInfoReadListener movieInfoReadListener = new MovieInfoReadListener(10, predicate, this::saveBatch);
EasyExcel.read(file, MovieForm.class, movieInfoReadListener).sheet(0).doReadSync();
}
private void saveBatch(List<MovieForm> list) {
log.info("批量插入:{}", list.size());
for (MovieForm item : list) {
log.info("item: {}", item);
}
}
3.2 自定义监听实现类
package com.example.common.listener;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.exception.ExcelAnalysisStopException;
import com.alibaba.excel.exception.ExcelDataConvertException;
import com.alibaba.excel.read.listener.ReadListener;
import com.example.res.EasyExcel.MovieForm;
import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
@Slf4j
public class MovieInfoReadListener implements ReadListener<MovieForm> {
/**
* 批次大小。
*/
private final int batchSize;
/**
* 缓存数据。
*/
private final List<MovieForm> cacheData;
/**
* 条件函数。
*/
private final Predicate<MovieForm> predicate;
/**
* 消费函数。
*/
private final Consumer<List<MovieForm>> consumer;
// 构造函数
public MovieInfoReadListener(int batchSize, Predicate<MovieForm> predicate, Consumer<List<MovieForm>> consumer) {
this.batchSize = batchSize;
this.predicate = predicate;
this.consumer = consumer;
this.cacheData = new ArrayList<>(this.batchSize);
}
@Override
// 每读取一行数据,就会调用一次
public void invoke(MovieForm data, AnalysisContext analysisContext) {
// 条件判断
if (!this.predicate.test(data)) {
return;
}
// 缓存数据添加
this.cacheData.add(data);
// 缓存数据大于当前批次,写入
if (this.cacheData.size() >= batchSize) {
this.consumer.accept(this.cacheData);
this.cacheData.clear();
}
}
@Override
// 数据读取完之后会执行
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
// 数据读取完成后,剩余数据不足一个批次,再次写入
if (this.cacheData.size() > 0) {
this.consumer.accept(this.cacheData);
}
}
/**
* 异常处理。
*
* @param exception 异常。
* @param context 上下文。
*/
@Override
public void onException(Exception exception, AnalysisContext context) throws Exception {
if (exception instanceof ExcelDataConvertException) {
ExcelDataConvertException excelDataConvertException = (ExcelDataConvertException) exception;
int rowIndex = excelDataConvertException.getRowIndex(); // 行索引。
int columnIndex = excelDataConvertException.getColumnIndex(); // 列索引。
String cellData = excelDataConvertException.getCellData().getStringValue(); // 单元格数据。
String reason = exception.getMessage(); // 异常原因。
log.error("第{}行,第{}列,单元格[{}],解析异常:{}", rowIndex + 1, columnIndex + 1, cellData, reason);
throw new ExcelAnalysisStopException();
}
throw exception;
}
}
3.3 导入业务场景:
3.3.1 分批次导入
3.3.2 条件过滤导入
提示:过滤出导入数据来源为2的数据