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

如何用Java从S3同目录分批按数量下载多文件

针对S3批量按数量限制下载小文件的优化方案

核心思路:分批次列举+批量下载控制

1. 改进listObjectsV2的分页与批量处理

listObjectsV2本身支持分页(通过MaxKeys参数控制单次列举数量),S3允许单次最大列举1000个对象,所以可以循环分页获取,每攒够20000个就启动一批下载任务,同时通过线程池控制并发数,避免内存过载。

  • 关键优化点:

    • 设置MaxKeys=1000,循环调用直到收集到足够数量的文件或列举完成
    • 每收集满20000个对象键后,提交独立的下载批次任务,同时清空当前列表,避免内存堆积
    • 根据机器CPU、网络资源调整线程池大小(建议50-100),避免无限制开线程
  • Java示例代码片段:

    AmazonS3 s3Client = AmazonS3ClientBuilder.defaultClient();
    ListObjectsV2Request listReq = new ListObjectsV2Request()
            .withBucketName("your-bucket")
            .withPrefix("target-directory/")
            .withMaxKeys(1000);
    
    List<String> objectKeys = new ArrayList<>();
    final int BATCH_SIZE = 20000;
    ExecutorService executor = Executors.newFixedThreadPool(80); // 按需调整
    
    do {
        ListObjectsV2Result listRes = s3Client.listObjectsV2(listReq);
        for (S3ObjectSummary obj : listRes.getObjectSummaries()) {
            objectKeys.add(obj.getKey());
            // 达到批量阈值则启动下载
            if (objectKeys.size() == BATCH_SIZE) {
                executor.submit(() -> downloadBatch(s3Client, objectKeys, "local-save-path"));
                objectKeys = new ArrayList<>(); // 重置列表
            }
        }
        listReq.setContinuationToken(listRes.getNextContinuationToken());
    } while (listRes.isTruncated());
    
    // 处理剩余不足一批的文件
    if (!objectKeys.isEmpty()) {
        executor.submit(() -> downloadBatch(s3Client, objectKeys, "local-save-path"));
    }
    
    executor.shutdown();
    executor.awaitTermination(1, TimeUnit.HOURS);
    

    配套的批量下载方法(可在批次内再用小线程池优化):

    private static void downloadBatch(AmazonS3 s3Client, List<String> keys, String localDir) {
        File dir = new File(localDir);
        if (!dir.exists()) dir.mkdirs();
    
        ExecutorService batchExecutor = Executors.newFixedThreadPool(20);
        for (String key : keys) {
            batchExecutor.submit(() -> {
                String fileName = key.substring(key.lastIndexOf("/") + 1);
                File localFile = new File(dir, fileName);
                s3Client.getObject(new GetObjectRequest("your-bucket", key), localFile);
            });
        }
        batchExecutor.shutdown();
        try {
            batchExecutor.awaitTermination(30, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
    

2. 用自定义过滤器限制TransferManager下载数量

transferManager.downloadDirectory默认会下载整个目录,但可以通过自定义S3ObjectFilter来控制下载数量,避免OutOfMemoryError:

  • Java示例代码片段:
    TransferManager tm = TransferManagerBuilder.standard().withS3Client(s3Client).build();
    AtomicInteger downloadCounter = new AtomicInteger(0);
    final int MAX_DOWNLOADS = 20000;
    
    MultipleFileDownload downloadTask = tm.downloadDirectory(
            "your-bucket",
            "target-directory/",
            new File("local-save-path"),
            new S3ObjectFilter() {
                @Override
                public boolean accept(S3ObjectSummary objectSummary) {
                    return downloadCounter.incrementAndGet() <= MAX_DOWNLOADS;
                }
            }
    );
    
    downloadTask.waitForCompletion();
    tm.shutdownNow();
    
    注意:如果需要多批次执行,要重置计数器,并通过调整前缀或延续标记跳过已下载的文件,保证下载顺序稳定。

3. 进一步性能优化建议

  • 启用S3 Transfer Acceleration:跨区域下载时,开启后可大幅提升小文件传输速度
  • 优化线程池参数:根据机器核心数、带宽调整线程数,避免过多线程导致上下文切换开销
  • 本地存储优化:使用SSD存储目标文件,减少磁盘写入延迟;机械硬盘可调整文件系统参数提升批量写入效率
  • 复用S3客户端:确保AmazonS3客户端单例复用,避免频繁创建连接的开销
  • 启用HTTP/2:新版AWS SDK默认支持HTTP/2,能提升并发小文件的传输效率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 19:16:17