专业的编程技术博客社区

网站首页 > 博客文章 正文

EasyExcel 整合 SpringBoot 导入数据处理:亲测有效

baijin 2024-09-09 00:59:54 博客文章 9 ℃ 0 评论

一、开始整合

(1)引入 Maven 依赖

<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.7.22</version>
</dependency>

<!-- easy excel -->
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>easyexcel</artifactId>
    <version>3.3.2</version>
</dependency>

<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

(2)创建测试对象类和准备测试 Excel 文件

import com.alibaba.excel.annotation.ExcelIgnore;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.annotation.format.DateTimeFormat;
import com.alibaba.excel.annotation.format.NumberFormat;
import com.alibaba.excel.enums.BooleanEnum;
import lombok.Data;

import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 测试导入DTO
 */
@Data
public class DemoDto {
    /** excel 的行号 */
    @ExcelIgnore
    private Long lineNum;
    /** 第一列:序号 */
    @ExcelProperty(value = "序号", index = 0)
    private Long indexNo;
    /** 第二列:名称 */
    @ExcelProperty(value = "名称", index = 1)
    private String name;
    /** 第三列:分数 */
    @ExcelProperty(value = "分数", index = 2)
    private Double score;
    /** 第四列:学费(小数转成字符串格式化 - 正确识别) */
    @ExcelProperty(value = "学费", index = 3)
    @NumberFormat("#.####")
    private String money;
    /** 第五列:入学如期 */
    @ExcelProperty(value = "入学如期", index = 4)
    @DateTimeFormat(value = "yyyy-MM-dd")
    private LocalDate entryDate;
    /** 第六列:缴费时间 */
    @ExcelProperty(value = "缴费时间", index = 5)
    @DateTimeFormat(value = "yyyy年MM月dd日HH时mm分ss秒", use1904windowing = BooleanEnum.TRUE)
    private LocalDateTime moneyDateTimeStr;

    /** 排名占比(%) */
    @ExcelProperty(value = "排名占比", index = 6)
    @NumberFormat("#.##%")
    private String rate;
}

测试文件图片


(3)扩展:自定义导入解析监听器

import cn.hutool.json.JSONUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.util.ListUtils;
import com.example.demo.dto.DemoDto;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;

import java.util.List;

@Slf4j
@Data
public class DemoListener implements ReadListener<DemoDto> {

    public DemoListener() {
    }
    // // 业务板块,用户保存数据到数据库
    //private BaseContractMaterialService baseContractMaterialService;
    //public DemoListener(BaseContractMaterialService baseContractMaterialService) {
    //    this.baseContractMaterialService = baseContractMaterialService;
    //}

    /**
     * 每隔100条存储数据库,实际使用中可以100条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 100;
    /**
     * 缓存的数据
     */
    private List<DemoDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

    @Override
    public void invoke(DemoDto demoDto, AnalysisContext analysisContext) {
        log.info("解析到一条数据:{}", JSONUtil.toJsonStr(demoDto));
        cachedDataList.add(demoDto);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (cachedDataList.size() >= BATCH_COUNT) {
            this.saveData();
            // 存储完成清理 list
            cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
        }
        //log.info("解析数据完成");
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        this.saveData();
        log.info("所有数据解析完成!");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() {
        log.info("解析到【{}】条数据,开始存储数据库!", cachedDataList.size());
        System.out.println("解析后的所有数据,如下所示:\n" + JSONUtil.toJsonStr(cachedDataList));
        log.info("存储数据库成功!");
    }
}

(4)测试案例

【1】 案例1:不使用监听器

/**
     * 最简单的读
     * <pre>
     * 1. 创建excel对应的实体对象 参照{@link DemoDto}
     * 2. 由于默认一行行的读取excel,所以需要创建excel一行一行的回调监听器,参照{@link DemoListener}
     * 3. 直接读即可
     * </pre>
     */
@Test
public void simpleRead() {
    log.info("==========================写法1 不需要创建监听器===========================");
    // 写法1:JDK8+ ,不用额外写一个DemoDataListener
    // since: 3.0.0-beta1
    //3.0.0版本之后,使用这种方法不需要创建监听器
    String fileName = "D:\\testDemoExcel.xlsx";
    List<DemoDto> dataItemList = new ArrayList<>();
    // 这里默认每次会读取100条数据 然后返回过来 直接调用使用数据就行
    // 具体需要返回多少行可以在`PageReadListener`的构造函数设置
    EasyExcel.read(fileName, DemoDto.class, new PageReadListener<DemoDto>(dataList -> {
        for (DemoDto demoData : dataList) {
            dataItemList.add(demoData);
            log.info("读取到一条数据{}", JSONUtil.toJsonStr(demoData));
        }
    })).sheet().doRead();

    System.out.println("解析后的所有数据,如下所示:\n" + JSONUtil.toJsonStr(dataItemList));
}

亲自测试结果:

【2】案例2:匿名内部类

@Test
public void simpleRead2() {
    log.info("==========================写法2 匿名内部类===========================");
    // 写法2:
    // 匿名内部类(创建一个监听器对象) 不用额外写一个DemoDataListener
    String fileName = "D:\\testDemoExcel.xlsx";
    // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
    EasyExcel.read(fileName, DemoDto.class, new ReadListener<DemoDto>() {
        /**
             * 单次缓存的数据量
             */
        public static final int BATCH_COUNT = 100;
        /**
             *临时存储
             */
        private List<DemoDto> cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);

        @Override
        public void invoke(DemoDto data, AnalysisContext context) {
            cachedDataList.add(data);
            if (cachedDataList.size() >= BATCH_COUNT) {
                saveData();
                // 存储完成清理 list
                cachedDataList = ListUtils.newArrayListWithExpectedSize(BATCH_COUNT);
            }
        }

        @Override
        public void doAfterAllAnalysed(AnalysisContext context) {
            saveData();
        }

        /**
             * TODO 加上存储数据库
             */
        private void saveData() {
            log.info("解析到【{}】条数据,开始存储数据库!", cachedDataList.size());
            System.out.println("解析后的所有数据,如下所示:\n" + JSONUtil.toJsonStr(cachedDataList));
            log.info("存储数据库成功!");
        }
    }).sheet().doRead();

}

测试结果:

【3】案例3:需要创建监听器

@Test
public void simpleRead3() {
    log.info("==========================写法3 需创建监听器===========================");
    // 有个很重要的点 ContractMaterialDataListener 不能被spring管理,要每次读取excel都要new,然后里面用到spring可以构造方法传进去
    // 写法3:
    //这种方法需要写一个监听器
    String fileName = "D:\\testDemoExcel.xlsx";
    // 这里 需要指定读用哪个class去读,然后读取第一个sheet 文件流会自动关闭
    EasyExcel.read(fileName, DemoDto.class, new DemoListener()).sheet().doRead();
}

亲自测试结果:

【4】案例4:读取单个文件多个 sheet

@Test
public void simpleRead4() {
    log.info("==========================写法4 读单文件多个sheet===========================");
    // 写法4
    //这种方法可以读一个文件里的多个sheet
    String fileName = "D:\\testDemoExcel.xlsx";
    // 一个文件一个reader
    try (ExcelReader excelReader = EasyExcel.read(fileName, DemoDto.class, new DemoListener()).build()) {
        // 构建一个sheet 这里可以指定名字或者no
        ReadSheet readSheet = EasyExcel.readSheet(0).build();
        // 读取一个sheet
        excelReader.read(readSheet);
    }
}

亲自测试结果:


本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表