[alibaba/easyexcel]分多个sheet读取的时候,第一个sheet的数据重复了,但是这个情况不是一致的,可能第二次导入就不重复了,

2024-05-11 902 views
4

触发Bug的代码


        //初始化监听器
        CustomListener workOrderListener = new CustomListener(workOrderService, fileVersionNo, now);
        //解析第一个sheet数据
        try {
            EasyExcel.read(file.getInputStream(), workOrderListener).sheet(0).doReadSync();
        } catch (Exception e) {
            return BaseResponse.error("期别计划sheet" + e.getMessage());
        }

        //解析第二个sheet数据
        try {
            EasyExcel.read(file.getInputStream(), workOrderListener).sheet(1).doReadSync();
        } catch (Exception e) {
            return BaseResponse.error("日别计划sheet" + e.getMessage());
        }

public class CustomListener extends AnalysisEventListener<Map<Integer, String>> {

    private final static Logger logger = LoggerFactory.getLogger(CustomListener.class);

    /**
     * 实际使用中可以3000条,然后清理list ,方便内存回收
     */
    private static final int BATCH_COUNT = 1000;

    @Resource
    private final CustomExcelService customExcelService;

    @Resource
    private String fileVersionNo;

    @Resource
    private Date now;

    /**
     * 用list集合保存解析到的结果
     */
    List<Map<Integer, Map<Integer, String>>> list = new ArrayList<>();

    /**
     * 重构,把传来的值赋给对应的属性
     *
     * @param customExcelService 自定义excel接口类
     */
    public CustomListener(CustomExcelService customExcelService, String fileVersionNo, Date now) {
        this.customExcelService = customExcelService;
        this.fileVersionNo = fileVersionNo;
        this.now = now;
        list = new ArrayList<>();
    }

    /**
     * 重写invokeHeadMap方法,获去表头,如果有需要获取第一行表头就重写这个方法,不需要则不需要重写
     *
     * @param headMap Excel每行解析的数据为Map<Integer, String>类型,Integer是Excel的列索引,String为Excel的单元格值
     * @param context context能获取一些东西,比如context.readRowHolder().getRowIndex()为Excel的行索引,表头的行索引为0,0之后的都解析成数据
     */
    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        logger.info("解析到一条头数据:{}, currentRowHolder: {}", headMap.toString(), context.readRowHolder().getRowIndex());
        Map<Integer, Map<Integer, String>> map = new HashMap<>();
        map.put(context.readRowHolder().getRowIndex(), headMap);
        list.add(map);
    }

    /**
     * 重写invoke方法获得除Excel第一行表头之后的数据,
     * 如果Excel第二行也是表头,那么也会解析到这里,如果不需要就通过判断context.readRowHolder().getRowIndex()跳过
     *
     * @param data    除了第一行表头外,数据都会解析到这个方法
     * @param context 和上面解释一样
     */
    @SneakyThrows
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext context) {
        logger.info("解析到一条数据:{}, currentRowIndex: {}----", data.toString(), context.readRowHolder().getRowIndex());
        Map<Integer, Map<Integer, String>> map = new HashMap<>();
        map.put(context.readRowHolder().getRowIndex(), data);
        list.add(map);
        // 达到BATCH_COUNT了,需要去存储一次数据库,防止数据几万条数据在内存,容易OOM
        if (list.size() >= BATCH_COUNT) {
            logger.info("保存数据开始---!");
            saveData();
            logger.info("保存数据结束---!");
            // 存储完成清理 list
            list.clear();
            logger.info("存储完成清理,list成功");
        }
    }

    /**
     * 解析到最后会进入这个方法,需要重写这个doAfterAllAnalysed方法,然后里面调用自己定义好保存方法
     *
     * @param context 解析上下文
     */
    @SneakyThrows
    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 这里也要保存数据,确保最后遗留的数据也存储到数据库
        saveData();
        logger.info("所有数据解析完成!");
        list.clear();
        logger.info("list数据清除成功");
    }

    /**
     * 加上存储数据库
     */
    private void saveData() throws Exception {
        logger.info("{}条数据,开始存储数据库!", list.size());
        customExcelService.saveForExcel(list, fileVersionNo, now);
        list.clear();
    }

提示的异常或者没有达到的效果

回答

3

我尝试降低BATCH_COUNT为1000

5

ExcelReader excelReader = null; try { excelReader = EasyExcel.read(fileName).build();

        // 这里为了简单 所以注册了 同样的head 和Listener 自己使用功能必须不同的Listener
        ReadSheet readSheet1 =
            EasyExcel.readSheet(0).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
        ReadSheet readSheet2 =
            EasyExcel.readSheet(1).head(DemoData.class).registerReadListener(new DemoDataListener()).build();
        // 这里注意 一定要把sheet1 sheet2 一起传进去,不然有个问题就是03版的excel 会读取多次,浪费性能
        excelReader.read(readSheet1, readSheet2);
    } finally {
        if (excelReader != null) {
            // 这里千万别忘记关闭,读的时候会创建临时文件,到时磁盘会崩的
            excelReader.finish();
        }
    }
0

参照多次读取的demo ,流不要多次读取。