SpringBooot整合Easy Excel:实现文件导入

#新星杯·14天创作挑战营·第11期#

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的数据
在这里插入图片描述

3.3.3 解析异常处理

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值