如何使用Java将Amazon S3上的目录压缩并留存Zip文件于S3
用Java将Amazon S3目录压缩为Zip并留存于S3的实现方案
完全可以实现,无需将所有文件下载到本地,通过流层面的直接处理就能完成,既节省本地存储,又提升效率。以下是基于AWS SDK for Java 2.x的具体实现步骤和代码示例:
核心思路
- 通过前缀过滤列出S3目标目录下的所有文件
- 创建Zip输出流并直接绑定到S3的目标Zip文件写入流
- 遍历每个S3文件,读取内容并写入Zip输出流,同时设置正确的Zip条目相对路径
- 自动关闭所有流,完成压缩操作
依赖准备(Maven)
<dependency> <groupId>software.amazon.awssdk</groupId> <artifactId>s3</artifactId> <version>2.20.0</version> <!-- 建议使用最新稳定版本 --> </dependency>
完整代码实现
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider; import software.amazon.awssdk.core.ResponseInputStream; import software.amazon.awssdk.core.sync.RequestBody; import software.amazon.awssdk.services.s3.S3Client; import software.amazon.awssdk.services.s3.model.GetObjectRequest; import software.amazon.awssdk.services.s3.model.ListObjectsV2Request; import software.amazon.awssdk.services.s3.model.ListObjectsV2Response; import software.amazon.awssdk.services.s3.model.PutObjectRequest; import software.amazon.awssdk.services.s3.model.S3Object; import java.io.IOException; import java.io.OutputStream; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class S3DirectoryZipper { // 替换为你的源bucket和目录前缀(必须以斜杠结尾) private static final String SOURCE_BUCKET = "your-source-bucket"; private static final String SOURCE_DIR_PREFIX = "path/to/target/dir/"; // 替换为目标bucket和Zip文件路径(可与源bucket相同) private static final String DEST_BUCKET = "your-destination-bucket"; private static final String DEST_ZIP_KEY = "compressed/result.zip"; public static void main(String[] args) { // 初始化S3客户端(自动从环境变量、凭证文件或IAM角色获取权限) try (S3Client s3Client = S3Client.builder() .credentialsProvider(DefaultCredentialsProvider.create()) .build()) { // 直接将Zip内容写入S3,无需本地临时文件 s3Client.putObject(PutObjectRequest.builder() .bucket(DEST_BUCKET) .key(DEST_ZIP_KEY) .build(), RequestBody.fromOutputStream(outputStream -> { try (ZipOutputStream zipOut = new ZipOutputStream(outputStream)) { // 分页列出目录下的所有文件(处理超过1000个文件的场景) ListObjectsV2Request listReq = ListObjectsV2Request.builder() .bucket(SOURCE_BUCKET) .prefix(SOURCE_DIR_PREFIX) .build(); ListObjectsV2Response listResp; do { listResp = s3Client.listObjectsV2(listReq); for (S3Object obj : listResp.contents()) { String objKey = obj.key(); // 跳过S3中的目录占位符对象(如果存在) if (objKey.equals(SOURCE_DIR_PREFIX)) { continue; } // 生成Zip条目的相对路径(去掉源目录前缀) String entryName = objKey.substring(SOURCE_DIR_PREFIX.length()); zipOut.putNextEntry(new ZipEntry(entryName)); // 读取S3文件内容并写入Zip try (ResponseInputStream<?> objInputStream = s3Client.getObject(GetObjectRequest.builder() .bucket(SOURCE_BUCKET) .key(objKey) .build())) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = objInputStream.read(buffer)) != -1) { zipOut.write(buffer, 0, bytesRead); } } zipOut.closeEntry(); } // 更新分页token,处理下一页结果 listReq = listReq.toBuilder() .continuationToken(listResp.nextContinuationToken()) .build(); } while (listResp.isTruncated()); } catch (IOException e) { throw new RuntimeException("Zip压缩失败", e); } }) ); System.out.println("Zip文件已成功生成:s3://" + DEST_BUCKET + "/" + DEST_ZIP_KEY); } catch (Exception e) { e.printStackTrace(); } } }
关键注意事项
- 权限配置:确保运行代码的实体(本地用户、EC2实例IAM角色等)拥有源bucket的
s3:GetObject权限和目标bucket的s3:PutObject权限 - 大文件/海量文件场景:如果需要处理大量文件或超大文件,建议改用分块上传(通过
TransferManager或手动实现Multipart Upload),避免内存溢出 - 线程安全:
ZipOutputStream不是线程安全的,若要并行读取S3文件,需对Zip写入操作加同步锁 - 错误重试:生产环境中建议添加AWS SDK的重试策略,处理网络波动或S3服务临时不可用的情况
- 目录占位符:S3没有真正的目录,若手动创建了目录占位符(大小为0的对象),代码中已跳过这类对象,避免Zip中出现空目录条目
内容的提问来源于stack exchange,提问作者YoHaRo
相关产品推荐
相关产品推荐

