EasyExcel优雅的实现文件上传

994 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 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 实现文件上传,工作必备技能,收藏点赞不后悔。

下期介绍下载导入模板。