多文件处理时触发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被正确关闭,前面的文件条目写入未完成,导致文件损坏。
修复步骤
- 确保输入流可重复读取:将每个文件的InputStream转为字节数组保存,或者修改
FileDto实现,让getContent()每次返回新的InputStream实例。 - 循环内关闭ZipEntry:每个文件写入完成后立即关闭对应的ZipEntry,保证条目写入完整。
- 用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
相关产品推荐
相关产品推荐

