alibaba / easyexcel

快速、简洁、解决大文件内存溢出的java处理Excel工具
https://easyexcel.opensource.alibaba.com
Apache License 2.0
31.96k stars 7.49k forks source link

使用ExcelWriter#fill方法进行填充list数据时,存在问题会使不同行的数据错乱 #3783

Open dani3lWong opened 4 months ago

dani3lWong commented 4 months ago

触发场景描述

1、使用List作为数据源,加上模板(e.g.{.name})进行数据写入时,如果list中其中一个map的数据有不存在的key,那一列的数据会上移。 2、后来经过试验发现,使用继承的VO,当传入的是父类时,对于缺少的属性也会有类似的情况。 简单模板: image

触发Bug的代码

1、map的Test用例

   import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.map.MapUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class TestMap {

    private static final Logger log = LoggerFactory.getLogger(TestMap.class);

    public static void main(String[] args) {

        File targetDir;
        if (args.length > 0 && FileUtil.exist(args[0])) {
            targetDir = FileUtil.file(args[0]);
        } else {
            targetDir = FileUtil.file("");
        }
        File targetFile = new File(targetDir, "targetMap.xlsx");
        if(FileUtil.exist(targetFile)) {
            FileUtil.del(targetFile);
        }
        FileUtil.touch(targetFile);

        List<Map<String, String>> list = new ArrayList<>();

        list.add(MapUtil.builder("name", "张三").put("score", "99").build());
        list.add(MapUtil.builder("name", "张四")/*.put("score", "98")*/.build());
        list.add(MapUtil.builder("name", "张五").put("score", "97").build());

        ExcelWriter writer = EasyExcel.write(targetFile)
                .excelType(ExcelTypeEnum.XLSX)
                .autoCloseStream(true)
                .withTemplate(new ClassPathResource("classpath://test.xlsx").getStream())
                .build();

        WriteSheet writeSheet = EasyExcel.writerSheet(0, "Sheet1")
                .build();

        FillConfig fillConfig = FillConfig.builder()
                .direction(WriteDirectionEnum.VERTICAL)
                .autoStyle(true)
                .build();

        writer.fill(list, fillConfig, writeSheet);
        writer.finish();

        log.info("文件输出至:{}", targetFile.getAbsolutePath());

    }

}

2、VO的测试用例:

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.resource.ClassPathResource;
import cn.hutool.core.map.MapUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class TestVO {

    private static final Logger log = LoggerFactory.getLogger(TestVO.class);

    public static void main(String[] args) {

        File targetDir;
        if (args.length > 0 && FileUtil.exist(args[0])) {
            targetDir = FileUtil.file(args[0]);
        } else {
            targetDir = FileUtil.file("");
        }
        File targetFile = new File(targetDir, "targetVO.xlsx");
        if(FileUtil.exist(targetFile)) {
            FileUtil.del(targetFile);
        }
        FileUtil.touch(targetFile);

        List<SupVO> list = new ArrayList<>();

        list.add(new SonVO("张三", "99"));
        list.add(new SupVO("张四"));
        list.add(new SonVO("张五", "97"));

        ExcelWriter writer = EasyExcel.write(targetFile)
                .excelType(ExcelTypeEnum.XLSX)
                .autoCloseStream(true)
                .withTemplate(new ClassPathResource("classpath://test.xlsx").getStream())
                .build();

        WriteSheet writeSheet = EasyExcel.writerSheet(0, "Sheet1")
                .build();

        FillConfig fillConfig = FillConfig.builder()
                .direction(WriteDirectionEnum.VERTICAL)
                .autoStyle(true)
                .build();

        writer.fill(list, fillConfig, writeSheet);
        writer.finish();

        log.info("文件输出至:{}", targetFile.getAbsolutePath());

    }

}

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

以上的测试用例都会得到相似的结果: image 附测试工作区: easyexcel-bug.zip

psxjoy commented 3 months ago

@zhuangjiaju 我看了一下源码,ExcelWriteFillExecutor中的doFill函数中有这样一个逻辑:

String variable = analysisCell.getVariableList().get(0);
if (!dataKeySet.contains(variable)) {
    continue;
}

这会导致空数据不进行写操作,直接进入下一行的数据操作。 如果可以的话,我希望认领这个问题并修复。 等待你的回复。