1. 引言

目前,在使用 AWS 管理控制台时,我们仅能一次下载 AWS S3 存储桶中的一个对象。但是,通过 SDK、CLI 或 CloudShell,我们可以一次下载多个对象。因此,如果需要下载整个 S3 存储桶,这些选项就是我们的选择。

本文将讨论如何使用 AWS 使用整个 S3 存储桶。

2. 使用 aws s3 sync

在我们的虚拟机上安装了 AWS CLI,或者在 CloudShell 中运行时,我们可以通过使用 aws s3 sync 命令复制整个 S3 存储桶:

$ aws s3 sync s3://baeldung-copy-entire-s3 .
download: s3://baeldung-copy-entire-s3/file.txt to ./file.txt

上述命令中,我们将名为 baeldung-copy-entire-s3 的存储桶复制到了当前工作目录。如果我们想要将其复制到不同的目录,只需将 . 替换为目标路径即可。

如果我们的默认区域与存储桶的区域不同,我们需要使用 --region 参数指定存储桶的区域:

$ aws s3 sync s3://baeldung-copy-entire-s3 . --region=eu-west-1

然后,如果我们要排除某个对象或一组对象,则可以将匹配模式传递给 --exclude 选项。--include 选项则相反。

2.1 复制大型存储桶

尽管 S3 对象的最大大小限制为 5TB,但 S3 存储桶没有大小限制。因此,在复制大型 S3 存储桶时,过程可能会很慢。然而,通过使用 aws configure 命令增加 max_concurrent_requests 可以加快速度:

$ aws configure set default.s3.max_concurrent_requests 500

默认情况下,max_concurrent_requests 的值为 10。当我们运行上述命令时,我们可以将其提高到 500。之后,我们将有更多并发下载请求 - 最多可达 500。

当然,除了 max_concurrent_requests,距离存储桶区域的接近程度以及网络带宽等因素也可能影响速度**。因此,在靠近我们所在区域工作的资源以及选择具有更快吞吐量的资源可以使事情更快。

使用虚拟私有云(VPC)也可以在将存储桶内的内容复制到同一区域的 EC2 实例时使事情更快。S3 转发加速是另一种加快速度的方式,但这将产生额外的成本。此外,其可用性目前有限。

3. 使用 Java SDK

目前,Java SDK 是唯一提供下载 S3 存储桶所有内容方法的 SDK

import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
import software.amazon.awssdk.services.s3.S3Client;
import software.amazon.awssdk.transfer.s3.S3TransferManager;
import software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest;
import java.nio.file.Paths;

public class Bucket {
    static final S3TransferManager transferManager = createTM();

    // Create Connection
    static S3TransferManager createTM() {
        S3Client.builder()
          .credentialsProvider(DefaultCredentialsProvider.create())
          .build();
        S3TransferManager transferManager = S3TransferManager.builder()
          .build();
        return transferManager;
    }

    static void downloadBucket(S3TransferManager transferManager, String destinationPath, String bucketName) {
        transferManager.downloadDirectory(DownloadDirectoryRequest.builder()
            .destination(Paths.get(destinationPath))
            .bucket(bucketName)
            .build())
          .completionFuture().join();
    }

    public static void main(String[] args) {
        downloadBucket(transferManager, ".", "baeldung-copy-entire-s3"); 
    } 
}

为了使用 Java SDK 复制我们的 S3 存储桶,我们需要以下内容:

  • DefaultCredentialsProvider 类(software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider)来为 S3Transfermanager 实例提供凭证
  • S3Client 工具(software.amazon.awssdk.services.s3.S3Client)用于 建立与 AWS S3 的客户端连接
  • S3Transfermanager 工具(software.amazon.awssdk.transfer.s3.S3TransferManager
  • DownloadDirectoryRequest 类(software.amazon.awssdk.transfer.s3.model.DownloadDirectoryRequest
  • Paths 类(java.nio.file.Paths)将下载目的地字符串转换为 Path

在示例代码中,我们在 Bucket 类中创建了 main 方法之外的两个方法:createTMdownloadBucket

createTM 方法用于创建与 Amazon S3 的客户端连接。然后,它构建了一个 S3TransferManager 实例并返回。

downloadBucket 方法中,downloadDirectory 方法创建了一个 DownloadDirectoryRequest 实例。然后,使用 S3TransferManager 实例,这个实例将存储桶中的所有对象下载到指定的目标位置。

当我们运行代码时,它将整个 S3 存储桶中的文件下载到项目目录的根目录。这是因为我们指定了 destinationPath“.”。当然,我们可以根据提供的适当字符串指定不同的路径。

在我们的示例中,我们在创建 S3 客户端连接时使用了 DefaultCredentialsProvider。但我们也可以使用自定义凭证提供程序

4. 使用 Python SDK

我们可以调整 Python SDK 来执行类似于使用 Java SDK 的操作。让我们尝试使用 Python SDK 下载整个 S3 存储桶:

import boto3
import os

s3 = boto3.client('s3')
bucket = boto3.resource('s3').Bucket('baeldung-copy-entire-s3')

def get_bucket():
    for object in bucket.objects.all():
        filename = os.getcwd() + '/' + object.key
        s3.download_file(bucket.name, object.key, filename)
get_bucket() 

在上述示例中,我们基本上遍历了存储桶中的所有对象。然后,我们将每个对象的键作为 key 参数传递给 S3 的 download_file 方法。这样,脚本就可以下载存储桶的所有内容。

我们可以通过调用 list_objects_v2 方法而不是 objects.all() 方法来实现,但无论哪种方式,我们都会使用 for loop。在这种情况下,如果存储桶包含许多对象,这种方法可能不太高效。当然,在这种情况下,使用 Java SDK 可能会更好。

5. 结论

在本文中,我们讨论了如何使用 AWS CLI、Java SDK 和 Python SDK 下载 S3 存储桶。我们应该记住,当复制大型 S3 存储桶时,Java SDK 方法可能更有效。