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

多文件处理时触发Stream Closed异常问题求助

问题:处理多文件压缩时触发Stream Closed异常

我遇到了Stream Closed异常:当需要处理多个文件时会触发该问题。调试后发现,第一个文件处理成功,但第二个文件在执行(length = fileDto.getContent().read(bytes))时无法继续处理,最终导致400错误。尝试添加try-catch块处理异常后,包含两个文件的压缩包可正常下载,但第二个文件已损坏。

相关代码如下:

try (ByteArrayOutputStream out = new ByteArrayOutputStream()) {
    ZipOutputStream zipOut = new ZipOutputStream(out);
    ...
    for (FileDto fileDto : listePdfFiles) {
        String fileBaseName = FilenameUtils.getBaseName(fileDto.getName());
        String fileExtension = "." + FilenameUtils.getExtension(fileDto.getName());
        if (duplicateFileName.containsKey(fileDto.getName())) {
            if (duplicateFileName.get(fileDto.getName()) > 0) {
                fileBaseName += "(" + duplicateFileName.get(fileDto.getName()) + ")";
            }
            duplicateFileName.put(fileDto.getName(), duplicateFileName.get(fileDto.getName()) + 1);
        }
        ZipEntry zipEntry = new ZipEntry(fileBaseName + fileExtension);
        zipOut.putNextEntry(zipEntry);

        byte[] bytes = new byte[1024];
        int length;
        while ((length = fileDto.getContent().read(bytes)) >= 0) {
            zipOut.write(bytes, 0, length);
        }
    }
    zipOut.closeEntry();
    zipOut.close();
    String random = String.format("%s", RandomStringUtils.randomAlphanumeric(8));
    try(InputStream archive = new ByteArrayInputStream(out.toByteArray())) {
        return FileDto.builder().content(archive)
                ...
    }
} catch (Exception e) {
    log.error("Exporting report to PDF error: ", e);
}

解决方案

问题根源

  • 输入流不可重复读取:第一个文件处理时已将fileDto.getContent()对应的InputStream读取至末尾,InputStream默认无法重复读取,二次调用read()会直接返回-1,甚至触发Stream Closed异常。
  • ZipEntry未及时关闭:zipOut.closeEntry()放在循环外部,仅最后一个ZipEntry被正确关闭,前面的文件条目写入未完成,导致文件损坏。

修复步骤

  1. 确保输入流可重复读取:将每个文件的InputStream转为字节数组保存,或者修改FileDto实现,让getContent()每次返回新的InputStream实例。
  2. 循环内关闭ZipEntry:每个文件写入完成后立即关闭对应的ZipEntry,保证条目写入完整。
  3. 用try-with-resources管理ZipOutputStream:避免手动关闭流引发的资源泄漏。

修复后代码

try (ByteArrayOutputStream out = new ByteArrayOutputStream();
     ZipOutputStream zipOut = new ZipOutputStream(out)) { // try-with-resources自动管理ZipOutputStream
    ...
    for (FileDto fileDto : listePdfFiles) {
        String fileBaseName = FilenameUtils.getBaseName(fileDto.getName());
        String fileExtension = "." + FilenameUtils.getExtension(fileDto.getName());
        if (duplicateFileName.containsKey(fileDto.getName())) {
            if (duplicateFileName.get(fileDto.getName()) > 0) {
                fileBaseName += "(" + duplicateFileName.get(fileDto.getName()) + ")";
            }
            duplicateFileName.put(fileDto.getName(), duplicateFileName.get(fileDto.getName()) + 1);
        }
        ZipEntry zipEntry = new ZipEntry(fileBaseName + fileExtension);
        zipOut.putNextEntry(zipEntry);

        // 将输入流转为字节数组,避免流耗尽问题
        byte[] fileContent;
        try (ByteArrayOutputStream tempOut = new ByteArrayOutputStream()) {
            byte[] bytes = new byte[1024];
            int length;
            while ((length = fileDto.getContent().read(bytes)) >= 0) {
                tempOut.write(bytes, 0, length);
            }
            fileContent = tempOut.toByteArray();
        }
        // 写入压缩包
        zipOut.write(fileContent);
        zipOut.closeEntry(); // 每个文件写入后立即关闭对应ZipEntry
    }

    String random = String.format("%s", RandomStringUtils.randomAlphanumeric(8));
    try(InputStream archive = new ByteArrayInputStream(out.toByteArray())) {
        return FileDto.builder().content(archive)
                ...
    }
} catch (Exception e) {
    log.error("Exporting report to PDF error: ", e);
}

额外说明

  • 若文件体积较大,将所有内容加载到内存可能引发OOM,建议修改FileDto实现,让getContent()从数据源(文件系统/数据库)重新获取新的InputStream实例。
  • 手动管理流时,必须保证每个ZipEntry在写入完成后及时关闭,否则压缩包内文件会损坏或缺失。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.25 07:52:37