[alibaba/easyexcel]导出大文件时,如果出现io异常导出失败,/tmp/poifiles目录下生成的xml临时文件不会删除

2024-04-30 554 views
8
问题描述

1.excel中1个sheet行上限是1048576,当导出数据行数超过时,会抛出io异常,此时产生的xml临时文件没有删除 2.当导出的字段数量比较多时,产生的临时文件占用比较大,下面的Demo中,100万条数据,占用大概1.6g,由于不会删除,因此会逐渐占满系统磁盘空间

image

断点打到最后,发现临时文件没有删除,当程序结束,也就是jvm关闭或者tomcat重启,临时文件才会删除。

image 测试代码
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.annotation.ExcelProperty;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.util.FileUtils;
import com.alibaba.excel.write.metadata.WriteSheet;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Getter
@Setter
class DemoData{

    @ExcelProperty(value = "id")
    private String id;

    @ExcelProperty(value = "商品")
    private String commodityName;

    @ExcelProperty(value = "商品2")
    private String commodityName2;

    @ExcelProperty(value = "商品3")
    private String commodityName3;

    @ExcelProperty(value = "商品4")
    private String commodityName4;

    @ExcelProperty(value = "商品5")
    private String commodityName5;

    @ExcelProperty(value = "商品6")
    private String commodityName6;

    @ExcelProperty(value = "商品7")
    private String commodityName7;

    @ExcelProperty(value = "商品8")
    private String commodityName8;

}

public class Testa {

    public static void main(String[] args) {

        System.out.println(FileUtils.getPoiFilesPath());
        String filePath = "/tmp/test.xlsx";
        ExcelWriter writer = EasyExcel.write(filePath).excelType(ExcelTypeEnum.XLSX).build();

        WriteSheet sheet = EasyExcel.writerSheet().head(DemoData.class).build();

        try{
            for (int i = 0; i < 10000; i++) {
                List<DemoData> rowData = new ArrayList<>();

                for (int j = 0; j < 110; j++) {
                    DemoData excelVo = new DemoData();
                    excelVo.setId("" + (i * 110 + j));
                    rowData.add(excelVo);
                }
                writer.write(rowData, sheet);
            }
            writer.finish();
        }catch (Exception e){
            e.printStackTrace();
        }

        System.out.println("导出执行完毕");

    }
}

回答

9

writer.finish(); 需要写到finnal里面去, 或者 用try with resouce

8

writer.finish(); 需要写到finnal里面去, 或者 用try with resouce

试过了,是不行的,你可以试下。 因为writer.write(rowData, sheet)这一步抛出了异常,内部已经调用了一次 writeContextImpl.finish(true),finish标识会被更新成true,后面再主动调用writer.finish()方法,不管是在哪里调用,都执行不到writeContextImpl.finish里面的逻辑了。

image
5

@wpj199582 可以看一下:https://github.com/alibaba/easyexcel/issues/2878#issue-1507165731,这是我之前提的issues writer.write(rowData, sheet)这一步抛出了异常,内部已经调用了一次 writeContextImpl.finish(true),finish标识会被更新成true,但是这个writeContextImpl.finish在大概357行会失败,如图: image

我猜测的原因是,发生异常后,原本应该执行临时文件清理逻辑,可是这个dispose方法内部会有一个flushRows方法,应该是尝试将流里的数据写到文件中: image

那么问题来了,我们遇到的是IO异常已经不支持往文件中写入数据了,于是这个flushRows方法就会再次抛出一个IO异常,后面的dispose方法中删除临时文件的逻辑都还没来得及执行。而且像你所说的,后续再手动调用writer.finish()方法也没用了,永远无法执行到临时文件的删除逻辑。

3

所以这确实是个bug,我现在只能暂时用官方提供的方法,压缩临时文件的大小了~