持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情
哈喽,大家好,我是一条。
之前写过一篇 1分钟用EasyExcel实现文件下载,简单优雅,方便高效。
有下载就要有上传,在实际开发中,这两个功能一般是“相生相依”,所以本文介绍使用 EasyExcel 优雅的实现文件上传。
前置知识
正式开始之前先简单介绍一下 Excel ,目前主要是 2003 和 2007 两个版本,其文件后缀名分别是 .xls 和 .xlsx 。
对于程序员,我们关注的是其底层的数据结构,把一个excel文件后缀改为 .zip ,打开之后就直接可以看到一个 excel 文件对应的 xml 格式的文件。
所以我们对于 Excel 文件的读取,实质是对 xml 的解析。
EasyExcel
首先还是引入 pom 坐标:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>easyexcel</artifactId>
<version>2.2.10</version>
</dependency>
其读取文件的原理首先需要一个监听器,通过监听器一行一行的读取数据,并对数据做一下转换,校验处理,最后入库。
实体类
和下载一样,使用@ExcelIgnore
和@ExcelProperty
注解,对用表头名称。
controller
@PostMapping("/upload")
public Result upLoadStore(MultipartFile file) throws IOException {
// 自定义监听器,注意不能交给 Spring 管理,需要自己 new
StoreUploadListener uploadListener = new StoreUploadListener(channelStoreService);
// 传入文件流 实体类 监听器
EasyExcel.read(file.getInputStream(), ChannelStorePO.class, uploadListener).sheet().doRead();
// 返回错误信息
return Result.ok(uploadListener.getErrorDataList());
}
listener
重点在我们自定义的 listener,需要继承 AnalysisEventListener
,并实现其invoke()
方法和doAfterAllAnalysed()
方法。
分别用来按行读取数据和所有数据读取完之后的操作。
考虑到实际业务,我们还需要记录错误信息,调用其他 service 的方法。所以代码框架如下:
@Data
public class StoreUploadListener extends AnalysisEventListener<ChannelStorePO> {
private ChannelStoreService channelStoreService;
//记录导入失败的数据信息
private List<String> errorDataList = new ArrayList<>();
private List<ChannelStorePO> channelStorePOS = new ArrayList<>();
public StoreUploadListener(ChannelStoreService channelStoreService) {
this.channelStoreService=channelStoreService;
}
@Override
public void invoke(ChannelStorePO channelStorePO, AnalysisContext analysisContext) {
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
}
}
注意事项:
- 对于导入数据过多的情况,不能全部读取完再插入,会有内存溢出的风险,一般3000条数据就需要存入一次。
- 如果在 service 层有查库行为,不能在 invoke 方法里调用,会导致每行数据查库一次,对数据库压力较大,应该全部读取完再做批量校验。
业务代码举例
@Override
public void invoke(ChannelStorePO channelStorePO, AnalysisContext analysisContext) {
try {
if (StringUtils.isNotBlank(channelStorePO.getOperationName())) {
channelStorePOS.add(channelStorePO);
}
} catch (Exception e) {
errorDataList.add(e.getMessage());
}
}
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
if (CollectionUtil.isNotEmpty(channelStorePOS)){
validateAndBuild(channelStorePOS);
validateStore(channelStorePOS);
if (CollectionUtil.isEmpty(errorDataList)) {
errorDataList.add("数据校验通过,本次共新增 " + channelStorePOS.size() + " 条数据!");
channelStoreService.batchInsertStore(channelStorePOS);
// new Thread(()->{
// log.info("--------后台线程启动!");
// channelStoreService.fixStoreBackGround();
// }).start();
}
}else {
errorDataList.add("上传数据为空");
}
}
总结
以上就是用EasyExcel 实现文件上传,工作必备技能,收藏点赞不后悔。
下期介绍下载导入模板。