如何将S3的binary/octet-stream内容打包进zip并解决流读取异常
问题根因
你的报错核心原因是getObject方法中错误使用了try-with-resources管理S3Client的生命周期:
- try-with-resources语法会在代码块执行结束后自动关闭声明的资源,也就是你拿到
ResponseInputStream返回值的瞬间,S3Client就被关闭了 - S3返回的
ResponseInputStream底层绑定了S3Client的网络连接,客户端关闭后连接直接断开,后续读取流的时候自然拿不到任何数据,才会报收到0字节的异常。
另外你当前的addFileToZip方法存在资源泄漏问题:只有抛出IO异常时才会关闭输入流,正常执行完成的场景没有关闭输入流。
解决方案
方案1:调整S3Client生命周期(适合大文件场景,避免全量读入内存)
修改getObject方法,不要在方法内关闭S3Client,将S3Client的生命周期交给上层逻辑管理:
public ResponseInputStream<GetObjectResponse> getObject(String key) { // 直接获取全局/上层管理的S3Client,不要用try-with-resources包裹 S3Client s3 = s3Client.getClient(); return s3.getObject( GetObjectRequest.builder() .bucket(bucketName) .key(key) .build() ); }
同时修改addFileToZip方法,统一管理输入流的关闭逻辑:
public static void addFileToZip(ZipOutputStream zip, String fileName, InputStream fileContent) throws IOException { // try-with-resources会在代码块结束后自动关闭输入流,无论成功还是异常 try (fileContent) { zip.putNextEntry(new ZipEntry(fileName)); IOUtils.copy(fileContent, zip); zip.closeEntry(); } }
方案2:先全量读取S3文件内容(适合小文件场景,逻辑更简单)
如果你的S3文件体积都不大,可以直接在getObject方法内把内容全量读取到内存后再返回,避免依赖S3Client的生命周期:
public byte[] getObjectContent(String key) throws IOException { // 这里依然可以用try-with-resources,读取完成后直接关闭客户端和输入流 try (S3Client s3 = s3Client.getClient(); ResponseInputStream<GetObjectResponse> is = s3.getObject( GetObjectRequest.builder() .bucket(bucketName) .key(key) .build() )) { return is.readAllBytes(); } }
调用压缩方法时传入字节数组对应的输入流即可:
addFileToZip(zipOut, fileName, new ByteArrayInputStream(getObjectContent(key)));
内容的提问来源于stack exchange,提问作者Denis Kisina
相关产品推荐
相关产品推荐

