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

Java POI递归写入字符串数组至XLSX遇流关闭错误求助

递归写入SXSSFWorkbook触发Stream closed错误的解决方法

问题描述

尝试通过递归函数将字符串数组形式的ResultSet逐行写入XLSX文件,运行时抛出以下错误:

java.lang.RuntimeException: java.io.IOException: Stream closed

定位到错误源自workbook.write()方法,需要解决递归场景下的操作拆分问题。

初始代码

import org.apache.poi.ss.usermodel.BorderStyle;
import org.apache.poi.ss.usermodel.Cell;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class XlsxService {

    private final Path outputFilePath;
    private SXSSFWorkbook workbook;
    private SXSSFSheet sheet;
    private Cell cell;
    private int rowNum = 0;

    public XlsxService(Path outputFilePath) {
        this.outputFilePath = outputFilePath;
    }

    public void generate(String[] resultSet) {
        if(workbook == null) {
            workbook = new SXSSFWorkbook();
            sheet = workbook.createSheet("Sheet");
        }
        writeToFile(resultSet);
        try (OutputStream fos = Files.newOutputStream(outputFilePath)) {
            workbook.write(fos);
            workbook.close();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    public void writeToFile(String[] params) {       
        Row dataRow = sheet.createRow(rowNum);
        for (int i = 0; i < params.length; i++) {
            dataRow.createCell(i).setCellValue(params[i]);         
        }
        rowNum++;
    }
}

更新后的代码

public void generate(String[] resultSet) {
        addRow(resultSet);
        save();
    }

    public void save() throws IOException {
        try (OutputStream fos = Files.newOutputStream(outputFilePath)) {
            workbook.write(fos);
        }
        workbook.close();
    }

    public void addRow(String[] params) {       
        Row dataRow = sheet.createRow(rowNum);
        for (int i = 0; i < params.length; i++) {
            dataRow.createCell(i).setCellValue(params[i]);         
        }
        rowNum++;
    }

ResultSet示例

String[] resultSet1 = {"A1", "B1", "C1"};
String[] resultSet2 = {"A2", "B2", "C2"};
...
String[] resultSetN = {"AN", "BN", "CN"};

错误原因

不管是初始还是更新后的代码,都犯了同一个核心错误:在递归过程中提前关闭了SXSSFWorkbook。

  • 每次调用generate都会执行save(),而save()里调用了workbook.close(),一旦workbook被关闭,后续递归调用时再操作workbook就会触发「流已关闭」的异常。
  • SXSSFWorkbook的write()方法本身不应该被多次调用(除非特殊配置),但最关键的问题是递归还未完成就释放了核心资源。

解决方法

拆分操作流程,将初始化、递归添加行、最终保存关闭三个环节彻底分离:

  1. 初始化workbook和sheet仅执行一次,避免递归中重复创建或判断。
  2. 递归函数只负责添加行数据,不涉及保存和资源释放。
  3. 所有递归执行完毕后,统一调用保存方法,一次性写入文件并关闭资源。

修改后的完整代码

import org.apache.poi.ss.usermodel.Row;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.IOException;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;

public class XlsxService {

    private final Path outputFilePath;
    private SXSSFWorkbook workbook;
    private SXSSFSheet sheet;
    private int rowNum = 0;

    public XlsxService(Path outputFilePath) {
        this.outputFilePath = outputFilePath;
        // 初始化工作簿和工作表,仅执行一次
        this.workbook = new SXSSFWorkbook();
        this.sheet = workbook.createSheet("Sheet");
    }

    // 递归处理行数据的入口方法,仅负责添加行
    public void processRow(String[] resultSet) {
        addRow(resultSet);
        // 这里添加你的递归逻辑,比如判断是否还有下一行数据,示例省略终止条件
        // if (hasNextResultSet()) {
        //     processRow(getNextResultSet());
        // }
    }

    // 所有行处理完成后,统一调用此方法完成写入和资源释放
    public void finalizeAndSave() throws IOException {
        try (OutputStream fos = Files.newOutputStream(outputFilePath)) {
            workbook.write(fos);
        } finally {
            // 先关闭工作簿
            workbook.close();
            // 清理SXSSF生成的临时文件,避免资源泄漏
            workbook.dispose();
        }
    }

    // 内部方法:添加单行数据
    private void addRow(String[] params) {
        Row dataRow = sheet.createRow(rowNum);
        for (int i = 0; i < params.length; i++) {
            dataRow.createCell(i).setCellValue(params[i]);
        }
        rowNum++;
    }
}

使用示例

// 实例化服务
Path outputPath = Path.of("output.xlsx");
XlsxService xlsxService = new XlsxService(outputPath);

// 模拟递归处理所有ResultSet(实际场景中替换为你的递归逻辑)
xlsxService.processRow(new String[]{"A1", "B1", "C1"});
xlsxService.processRow(new String[]{"A2", "B2", "C2"});
xlsxService.processRow(new String[]{"A3", "B3", "C3"});

// 所有行处理完毕后,统一保存
xlsxService.finalizeAndSave();

关键注意点

  • 递归过程中绝对不能关闭workbook或OutputStream,否则后续操作会触发流关闭异常。
  • SXSSFWorkbook会生成临时文件来优化内存,务必调用dispose()清理这些文件,防止磁盘空间泄漏。
  • 初始化操作放在构造函数中,确保只执行一次,避免递归中重复创建资源。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 16:44:56