总结:目前市面上流行的2种 EasyExcel和POI都不是真正的对物理excel文件进行追加导入。只是在缓存里面追加,最后一次性写入,并不能解决内存占用问题。

无非就是下面两种逻辑:
1.for循环查询数据,将数据写入缓存,最后一次性写入excel。
2.将已有的excel通过FIleInputStream流读出来,加载到内存当中,然后获取对应sheet页的行数,进行追加操作。

PS:建议使用CSV文件格式,直接使用Java原生的FileUtils追加写入

FileUtils.writeStringToFile(file, sb.toString(), "GBK", true);

下面来讲讲easyExcel、POI、csv相关的代码写法:

要求:
1.对表头进行排序过滤
2.对列进行排序过滤

1.EasyExcel

官网地址:EasyExcel官网

我们使用新版本的,旧的对于字段排序/过滤都不支持
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>easyexcel</artifactId>
      <version>3.3.2</version>
    </dependency>

//此为线程池异步导出
commonExecutor.execute(() -> {
   File file = null;
   ExcelWriter excelWriter = null;
   try {
       file = File.createTempFile("a", ".xlsx");
       file.deleteOnExit();
       excelWriter = EasyExcel.write(file, UserVO.class)
               .head(headlist)  // 表头,传入一个list集合
               .includeColumnFieldNames(showColumnList) // 对于UserVO要展示的列集合
               .orderByIncludeColumn(true) // 是否根据showColumnList集合的顺序排序
               .autoCloseStream(true) // 自动关闭流
               .build();
       WriteSheet writeSheet = EasyExcel.writerSheet("Sheet1").build();
       // 循环 200/页查询数据
       int pageNum = 1;
       int pageSize = 200;
       while (true) {
           List<UserVO> voList = getData(pageNum, pageSize); // getData根据业务实现
           // 将数据写入临时文件 此处循环追加就是写到内存里面
           excelWriter.write(voList , writeSheet);
           if (pageNum * pageSize >= count) {
               break;
           }
           pageNum++;
       }
       // 数据处理完毕后,刷盘写入物理文件
       excelWriter.finish();
   } catch (Exception e) {
       log.error("导出 异常:" + e.getMessage(), e);
   } finally {
       // 删除临时文件
       if (!Objects.isNull(file)) {
           file.delete();
       }
   }
});

2.POI

网传SXSSFWorkbook可以实现追加写入,然后实操后,其实是覆盖写入

//SXSSFWorkbook 和 XSSFWorkbook 都差不多,只是定义不同,写法都差不多

import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;

public class ExcelAppender {
    public static void main(String[] args) {
        String filePath = "C:\\Local\\Temp\\11.xlsx";

        // 构造一个查询数据的列表
        List<String> queryDataList = List.of("query4", "query5", "query6");

        // 加载已存在的 Excel 文件
        try (Workbook workbook = new SXSSFWorkbook(new FileInputStream(new File(filePath)))) {
            Sheet sheet = workbook.getSheetAt(0);

            // 获取已存在数据的最后一行索引
            int lastRowNum = sheet.getLastRowNum();

            // 在最后一行索引的下一行开始追加写入查询数据
            int rowNum = lastRowNum + 1;
            for (String queryData : queryDataList) {
                Row row = sheet.createRow(rowNum++);
                Cell cell = row.createCell(0);
                cell.setCellValue(queryData);
            }

            // 保存修改后的工作簿到文件
            try (FileOutputStream outputStream = new FileOutputStream(new File(filePath))) {
                workbook.write(outputStream); 
                System.out.println("查询数据已覆盖写入到 Excel 文件:" + filePath);
            } catch (IOException e) {
                e.printStackTrace();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

3.CSV

来写我最终选择的写法CSV,就是追加物理文件数据。很不错。

commonExecutor.execute(() -> {
   File temp = null;
    try {
        temp = File.createTempFile("aaa", ".csv");
        temp.deleteOnExit();
        List<List<String>> fillData = new ArrayList<>();
        // 1.初始化表头,showNameList是表头集合,有顺序的哈~
        fillData.add(ImmutableList.of(showNameList.toString()));
        // 2.循环 200/页查询数据
        int pageNum = 1;
        int pageSize = 200;
        while (true) {
            List<UserVO> recordVOList = getData(pageNum, pageSize);
            // 3.填充业务数据 我用的反射
            fillData.addAll(ExcelUtil.convertSortEntityListToDataList(showColumnList, recordVOList));

            StringBuilder sb = new StringBuilder();
            for (List<String> rowData : fillData) {
                sb.append(String.join(",", rowData));
                sb.append(System.lineSeparator());
            }
            try {
                FileUtils.writeStringToFile(temp, outPutStr, "GBK", true);
            } catch (IOException e) {
                log.error("写入CSV 异常:{}", e.getMessage(), e);
                return;
            }

            if (pageNum * pageSize >= count) {
                break;
            }
            pageNum++;
            fillData.clear();
        }
    } catch (Exception e) {
        log.error("导出 异常:{}", e.getMessage(), e);
    } finally {
        // 4.删除临时文件
        if (!Objects.isNull(temp)) {
            temp.delete();
        }
    }
});

当然,会存在一定的问题,比如csv文件打开时,office全家桶会把日期格式转换,比如:
“2024-01-04 14:28:29” -> “2024/1/4 14:28:29”
就看大家能不能接受了。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐