SpringBoot集成EasyExcel实现文件上传

前言

前段时间刚刚在一个项目中集成EasyExcel实现了数据导出到Excel表中,“产品经理”突然又加了要求:“那个谁,这个项目好像还缺一个批量数据导入的功能,你去写一下”。我:“……😥😤”。生活所迫,只能“自愿”的去完成了这个功能。

image-20220503191202029

EasyExcel官网 – > https://www.yuque.com/easyexcel/doc/easyexcel

1、实体类准备

这里用一个简单的学生Student类进行演示

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Student implements Serializable {

    private static final long serialVersionUID = 1L;

    @ExcelProperty(value = "学号")
    private String studentId;

    @ExcelProperty(value = "姓名")
    private String studentName;

    @ExcelProperty(value = "年级")
    private String studentGrade;

    @ExcelProperty(value = "专业")
    private String studentMajor;

    @ExcelProperty(value = "班级")
    private String studentClass;
}

2、自定义监听器

根据白嫖来的官网教学,我们还需要自定义一个Excel读的监听器。这里设定了每隔100条存储数据库,然后清理list,这是为了防止数据几万条数据在内存,从而造成OOM(OutOfMemory,内存溢出)。

这里使用到了泛型提高了这个监听器的复用性,也可以将下面的T换成自己指定的一个实体类

@Slf4j
@Setter
@EqualsAndHashCode(callSuper = true)
public class DataListener<T> extends AnalysisEventListener<T> {
    /** 每隔100条存储数据库,然后清理list ,方便内存回收*/
    private static final int BATCH_COUNT = 100;

    /** 需要传递一个service进来对业务逻辑进行操作,即存放数据进数据库中 */
    private IService<T> service;

    /** 每次创建Listener的时候把service传进来 */
    public DataListener(IService<T> service){
        this.service = service;
    }

    /** 缓存的数据 */
    List<T> list = new ArrayList<T>();

    /**
     * @description 读取数据所执行的逻辑
     * @author xBaozi
     * @date 17:14 2022/5/3
     **/
    @Override
    public void invoke(T instance, AnalysisContext analysisContext) {
        log.info("读取到了数据{}", instance);
        list.add(instance);
        // 达到BATCH_COUNT存储一次数据库,防止数据几万条数据在内存造成OOM
        if (list.size() >= BATCH_COUNT){
            this.saveData();
            list.clear();
        }
    }

    /**
     * @description 所有数据读取完毕之后将数据存放到数据库中
     * @author xBaozi
     * @date 17:15 2022/5/3
     **/
    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 确保最后遗留的数据也存储到数据库
        this.saveData();
        log.info("所有数据导入成功");
    }

    /**
     * @description 将数据存放到数据库中
     * @author xBaozi
     * @date 17:09 2022/5/3
     **/
    private void saveData() {
        for (T data : list) {
            log.info("{}开始存放数据库……", data);
            try {
                this.service.save(data);
            } catch (Exception e) {
                log.info(e.getMessage());
            }
        }
    }
}

3、编写控制器

在上面的两个准备工作弄好之后,就可以编写控制器对其进行使用实现我们的需求了

/**
 * @description 实现数据批量导入
 * @author xBaozi
 * @date 19:27 2022/5/3
 * @param file  需要导入的文件
 **/
@PostMapping("/upload")
public Result<String>  upload(@RequestParam("files") MultipartFile file) throws IOException {
    // 获得上传文件的名称
    String filename = file.getOriginalFilename();
    // 获取源文件的后缀名
    int index = filename != null ? filename.lastIndexOf(".") : -1;
    // 对没有后缀名的文件进行判断
    if (index < 0) {
        return new Result.error("请选择正确的文件");
    }
    // 截取.点后面的内容,即后缀名
    String suffix = filename.substring(index + 1);
    log.info("获取后缀名为:{}", suffix);
    //判断是否为excel文档
    if (!"xlsx".equals(suffix) && !"xls".equals(suffix)) {
        return new Result.error("只支持Excel文档导入");
    }
    try {
        EasyExcel.read(file.getInputStream(), Student.class, new DataListener<Student>(studentService)).sheet().doRead();
    } catch (Exception e) {
        return new Result.error("导入失败,请重试");
    }
    return new Result.success("批量导入成功");
}

4、结果演示

  1. 根据实体类创建导入数据。

    这里可能有些伙伴有疑问,这里为什么会有一个序号呢?实体类里面没有需要这个属性啊,这是因为在编写实体类的时候是按照value进行匹配,而不是根据index进行匹配的,一次在导入时只会匹配实体类中存在的value。

image-20220503193322699

  1. 使用Postman或Apifox进行测试(这里使用的时Apifox)

image-20220503193857971

5、问题总结

狗子我在写这一个功能时遇到了一个bug,就是在读取Excel文件时,读取到的数据都是null,在排查了好一段时间的问题之后可算是给我找到了问题所在,原来是EasyExcel本身的问题,它与我使用的Lombok产生了冲突。

EasyExcel和Lombok会有一个冲突:当你尝试在用于接收Excel解析数据的Bean上面加上@Accessors(chain = true)注解时,你会发现该Bean接收不到数据,体现在String类型的字段总是为null,int类型的字段总是为0。

因此目前的解决办法就是,把@Accessors(chain = true)这个注解先给删掉,鱼与熊掌不可兼得,只能放弃其一了。

  • 6
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

陈宝子

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值