You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在使用Apache POI StreamingReader读取大Excel时高效同步写入?

基于Apache POI实现大Excel文件的边读边写(带错误列追加)

针对大Excel文件的处理,核心思路是用流式读取逐行解析原文件,同时用流式写入逐行生成新文件,既不加载整个文件到内存(解决OOM问题),也不用缓存全量数据(解决写入耗时问题)。以下是两种可行方案:

方案一:第三方StreamingReader + POI SXSSFWorkbook

利用你已经在使用的StreamingReader做流式读,搭配POI官方的SXSSFWorkbook做流式写,代码简洁易维护:

// 初始化流式写对象:内存中保留100行数据,超过则刷到临时文件
SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(100);
// 初始化流式读对象:配置缓存和缓冲区大小,可按需调整
StreamingReader reader = StreamingReader.builder()
        .rowCacheSize(100)
        .bufferSize(4096)
        .open(new FileInputStream("你的输入文件路径.xlsx"));

// 创建和原表同名的目标工作表
SXSSFSheet targetSheet = sxssfWorkbook.createSheet(reader.getSheetName());

int rowIndex = 0;
for (Row sourceRow : reader) {
    SXSSFRow targetRow = targetSheet.createRow(rowIndex);
    int sourceCellCount = sourceRow.getLastCellNum();

    // 复制原行的所有单元格内容
    for (int colIndex = 0; colIndex < sourceCellCount; colIndex++) {
        Cell sourceCell = sourceRow.getCell(colIndex);
        SXSSFCell targetCell = targetRow.createCell(colIndex);
        if (sourceCell != null) {
            // 简单复制单元格值,如需保留样式可额外处理(注意SXSSF样式复用)
            targetCell.setCellValue(sourceCell.toString());
        }
    }

    // 执行你的数据验证逻辑,获取错误信息
    String errorMsg = validateRowData(sourceRow);
    // 追加"Error Message"列
    SXSSFCell errorCell = targetRow.createCell(sourceCellCount);
    errorCell.setCellValue(errorMsg != null ? errorMsg : "");

    rowIndex++;
    // 每处理1000行手动刷盘,进一步降低内存占用
    if (rowIndex % 1000 == 0) {
        ((SXSSFSheet) targetSheet).flushRows();
    }
}

// 写出到目标输出流(比如HTTP响应流或本地文件)
sxssfWorkbook.write(new FileOutputStream("你的输出文件路径.xlsx"));
// 清理SXSSF的临时文件,避免磁盘占用
sxssfWorkbook.dispose();
reader.close();

注意事项

  • 样式处理:如果原Excel有复杂样式,直接复制CellStyle可能导致内存溢出,建议复用SXSSF的样式对象,避免重复创建。
  • 公式处理:StreamingReader默认读取公式计算后的结果,如需保留原公式,需在读取时额外配置。

方案二:POI原生SAX解析 + SXSSFWorkbook

如果不想依赖第三方库,用POI原生的SAX事件模型做流式读,配合SXSSF写,原理和方案一一致,但代码更繁琐:

SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(100);
SXSSFSheet targetSheet = sxssfWorkbook.createSheet("Sheet1");

// 打开原Excel包
OPCPackage pkg = OPCPackage.open(new File("你的输入文件路径.xlsx"));
XSSFReader xssfReader = new XSSFReader(pkg);
SharedStringsTable sst = xssfReader.getSharedStringsTable();

// 配置SAX解析器
XMLReader parser = XMLReaderFactory.createXMLReader();
parser.setContentHandler(new XSSFSheetXMLHandler(sst, new XSSFSheetXMLHandler.SheetContentsHandler() {
    private int currentRowNum = -1;
    private SXSSFRow currentTargetRow;
    private List<String> currentRowValues = new ArrayList<>();

    @Override
    public void startRow(int rowNum) {
        currentRowNum = rowNum;
        currentRowValues.clear();
        currentTargetRow = targetSheet.createRow(rowNum);
    }

    @Override
    public void cell(String cellRef, String cellValue, XSSFComment comment) {
        currentRowValues.add(cellValue);
        // 写入原单元格值
        int colNum = CellReference.convertColStringToIndex(cellRef.substring(0, 1));
        SXSSFCell targetCell = currentTargetRow.createCell(colNum);
        targetCell.setCellValue(cellValue);
    }

    @Override
    public void endRow(int rowNum) {
        // 执行数据验证
        String errorMsg = validateRowData(currentRowValues);
        // 追加错误列
        SXSSFCell errorCell = currentTargetRow.createCell(currentRowValues.size());
        errorCell.setCellValue(errorMsg != null ? errorMsg : "");

        // 定期刷盘
        if (rowNum % 1000 == 0) {
            ((SXSSFSheet) targetSheet).flushRows();
        }
    }
}, null, false));

// 开始解析工作表
InputStream sheetIn = xssfReader.getSheetsData().next();
parser.parse(new InputSource(sheetIn));

// 写出并清理资源
sxssfWorkbook.write(new FileOutputStream("你的输出文件路径.xlsx"));
sxssfWorkbook.dispose();
pkg.close();
sheetIn.close();

方案对比

  • 方案一:代码简洁,依赖第三方库但封装完善,开发效率高,适合大多数场景。
  • 方案二:纯原生POI实现,无额外依赖,代码复杂度高,适合对依赖有严格要求的场景。

内容的提问来源于stack exchange,提问作者procrastinator1771

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.13 22:47:12