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

如何使用Java将Amazon S3上的目录压缩并留存Zip文件于S3

用Java将Amazon S3目录压缩为Zip并留存于S3的实现方案

完全可以实现,无需将所有文件下载到本地,通过流层面的直接处理就能完成,既节省本地存储,又提升效率。以下是基于AWS SDK for Java 2.x的具体实现步骤和代码示例:

核心思路

  1. 通过前缀过滤列出S3目标目录下的所有文件
  2. 创建Zip输出流并直接绑定到S3的目标Zip文件写入流
  3. 遍历每个S3文件,读取内容并写入Zip输出流,同时设置正确的Zip条目相对路径
  4. 自动关闭所有流,完成压缩操作

依赖准备(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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 08:59:20