EasyExcel
EasyExcel 概述
Java 解析、生成 excel 比较有名的框架有 Apache poi、jxl,但他们都存在一个严重的问题就是非常耗内存,easyExcel 也是用来解析 excel 用的,但是他却解决了这个问题
EasyExcel 的原理
写数据
创建模型,在创建模型之前在 service_video 模型中添加单元测试和热加载相关的依赖,如下
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
然后在引入 easyExcel 相关的依赖
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.1.1</version>
</dependency>
</dependencies>
模型内容如下
public class StudentData {
(value = "学号", index = 0)
private Integer no;
(value = "姓名", index = 1)
private String name;
}
写数据,也就是导出 excel 文件
/**
* @author BNTang
* @version S2.3.2Dev
* @program video_parent
* @date Created in 2021/4/3 22:08
* @description
**/
public class ServiceVideoApplicationTests {
public void writeExcel() {
String fileName = "D:\\01.xlsx";
List<StudentData> studentDataList = new ArrayList<>();
for (int i = 0; i < 10; i++) {
StudentData studentData = new StudentData();
studentData.setNo(i);
studentData.setName("BNTang" + i);
studentDataList.add(studentData);
}
/*
* 1.fileName:文件路径与名称
* 2.StudentData.class:模型的字节码
* 3.sheet:名称
* 4.doWrite:要写的数据,是一个list
*/
EasyExcel.write(fileName, StudentData.class).sheet("学生").doWrite(studentDataList);
}
}
运行测试类,最终结果如下图所示
读数据
创建监听器
public class ExcelListener extends AnalysisEventListener<StudentData> {
/**
* <p>
* 一行一行读取数据
* </p>
*
* @param studentData 读取到的一行数据模型对象
* @param analysisContext analysisContext
*/
public void invoke(StudentData studentData, AnalysisContext analysisContext) {
System.out.println("data = " + studentData);
}
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
System.out.println("表头: " + headMap);
}
/**
* <p>
* 所有的数据读取完毕之后会自动调用
* </p>
*
* @param analysisContext analysisContext
*/
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
System.out.println("读取完毕");
}
}
读数据
public void ReadExcel() {
String fileName = "D:\\01.xlsx";
// 每读取一行数据的时候, 就会调用监听器当中的方法
EasyExcel.read(fileName, StudentData.class, new ExcelListener()).sheet().doRead();
}
应用到工程当中
建立模型
/**
* @author BNTang
* @version S2.3.2Dev
* @program video_parent
* @date Created in 2021/4/4 2:22
* @description 分类excel上传的模型类
**/
public class CategoryData {
(index = 0)
private String oneCategoryData;
(index = 1)
private String twoCategoryData;
}
创建监听器
/**
* @author BNTang
* @version S2.3.2Dev
* @program video_parent
* @date Created in 2021/4/4 2:25
* @description 分类管理上传excel监听器
**/
public class CategoryExcelListener extends AnalysisEventListener<CategoryData> {
public void invoke(CategoryData categoryData, AnalysisContext analysisContext) {
}
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
编写控制器,用来接收文件,然后编写对应文件处理的业务
/**
* @author BNTang
* @version S2.3.2Dev
* @program video_parent
* @date Created in 2021/4/4 2:27
* @description 分类管理控制器
**/
("/service_video/category")
public class CategoryController {
private final CategoryService categoryService;
public CategoryController(CategoryService categoryService) {
this.categoryService = categoryService;
}
("/addCategory")
public ResponseResult addCategory(MultipartFile file) {
// 调用业务上传excel
categoryService.saveCategory(file);
return ResponseResult.ok();
}
}
如上报错是因为还没有编写对应的接口和实现类所以找不到下面我们就来编写,在继续往下写之前我先来弥补一下我之前的一个错误就是把 mp 的代码生成器给删除了,这个时候要生成分类管理表相关的实体了,所以这个时候把之前的生成器代码拿回来放到测试包下,然后改一下生成的表名,把之前新建的分类管理的 Controller
删除用 mp 生成器生成的
改好了之前点击启动生成即可,这个类以后就保留着,然后把如上 Controller 写的内容复制到 mp 生成器生成的 Controller 中去即可
修改对应的业务类,修改 CategoryService
接口,也就是分类管理的接口
/**
* <p>
* 上传excel
* </p>
*
* @param file 上传的文件
*/
void saveCategory(MultipartFile file);
然后就是编写业务类了,内容如下
/**
* <p>
* 科目分类 服务实现类
* </p>
*
* @author BNTang
* @since 2021-04-04
*/
public class CategoryServiceImpl extends ServiceImpl<CategoryMapper, Category> implements CategoryService {
/**
* excel监听器
*/
private final CategoryExcelListener categoryExcelListener;
public CategoryServiceImpl(CategoryExcelListener categoryExcelListener) {
this.categoryExcelListener = categoryExcelListener;
}
public void saveCategory(MultipartFile file) {
// 读取excel
try {
EasyExcel.read(file.getInputStream(), CategoryData.class, categoryExcelListener).sheet().doRead();
} catch (IOException e) {
e.printStackTrace();
}
}
}
剩余的内容就是完善一下我们 excel 上传解析的监听器了,主要就是在监听器当中保存数据,最终监听器代码如下
/**
* @author BNTang
* @version S2.3.2Dev
* @program video_parent
* @date Created in 2021/4/4 2:25
* @description 分类管理上传excel监听器
**/
public class CategoryExcelListener extends AnalysisEventListener<CategoryData> {
private final CategoryService categoryService;
public CategoryExcelListener(CategoryService categoryService) {
this.categoryService = categoryService;
}
public void invoke(CategoryData categoryData, AnalysisContext analysisContext) {
// 写入到数据库当中
if (!Objects.isNull(categoryData)) {
// 如果一级分类不存在,就保存到数据库当中
Category oneCategory = this.isExistOneCategory(categoryData);
if (Objects.isNull(oneCategory)) {
oneCategory = new Category();
oneCategory.setTitle(categoryData.getOneCategoryData());
oneCategory.setParentId("0");
categoryService.save(oneCategory);
}
// 保存2级分类,先判断2级分类是否已经存在
Category twoCategory = this.isExistTwoCategory(categoryData, oneCategory.getId());
if (Objects.isNull(twoCategory)) {
twoCategory = new Category();
twoCategory.setTitle(categoryData.getTwoCategoryData());
twoCategory.setParentId(oneCategory.getId());
categoryService.save(twoCategory);
}
}
}
/**
* <p>
* 判断1级分类是否已经存在
* </p>
*/
private Category isExistOneCategory(CategoryData categoryData) {
QueryWrapper<Category> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("title", categoryData.getOneCategoryData());
queryWrapper.eq("parent_id", "0");
return categoryService.getOne(queryWrapper);
}
/**
* <p>
* 判断2级分类是否已经存在
* </p>
*/
private Category isExistTwoCategory(CategoryData categoryData, String pid) {
QueryWrapper<Category> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("title", categoryData.getTwoCategoryData());
queryWrapper.eq("parent_id", pid);
return categoryService.getOne(queryWrapper);
}
/**
* <p>
* 所有的数据读取完毕之后会自动调用
* </p>
*/
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
log.info("上传分类 excel 解析完毕!");
}
}
如上主要的业务就是先看看数据库存不存在当前的分类有就不添加没有就添加,核心的方法为如下
然后启动工程发现出现了循环依赖注入的问题
为啥会出现这个问题呢,可参考:https:///深入理解Spring循环依赖.html
修改所有注入的地方都改为 @Resource
的方式即可
启动工程上传模板发现,报错了,模板的内容如下图所示
很明显就是我们少添加了自动填充的实体类配置
修改实体类如下
然后在修改我们之前的分类 Controller 因为少加了一些内容加的内容如下
再次重启测试效果如下图所示