能否批量上传到Amazon S3?

99

亚马逊S3支持批量上传吗?我有一个任务,需要每晚上传约100K个文件,可达到1G的大小,但强烈偏向于小文件(90%的文件小于100字节,99%的文件长度小于1000字节)。

S3 API是否支持在单个HTTP调用中上传多个对象?

所有对象必须作为单独的对象在S3中可用。 我不能将它们放在其他地方(FTP等)或以另一种格式(数据库,EC2本地驱动器等)进行托管。这是我无法更改的外部要求。


我可以问这些问题吗? - Alex Gordon
我想知道为什么会出现这样的要求。如果您需要一次性替换所有文件,也许有一些方法可以将它们定期上传到临时存储桶中,然后更改存储桶名称? - Eugene Mayevski 'Callback
你可以看一下 JetS3t,它在 S3 同步和多线程方面功能非常完整。 - Thibault D.
5
这个问题的被接受答案还有效吗?已经有5年了,只是好奇在这段时间里是否有什么变化... - Abe Miessler
7个回答

52

或者,您可以使用同步命令通过 AWS CLI 工具将 S3 上传。

aws s3 sync local_folder s3://bucket-name

您可以使用此方法快速批量上传文件到S3。


27
和之前的回答一样,这里的暗示似乎是这些工具在某种程度上做了一些 API 无法实现的事情,但我不认为情况是这样的。 - bridiver
我目前正在使用AWS CLI工具在本地目录和S3存储桶之间进行同步。我想知道是否有一个参数或参数可以用来将“上传”或同步结果输出到本地TXT文件,然后通过blat.exe发送电子邮件给某人。 所有这些都要放入批处理文件中,以便定期同步数千个文件,这些文件将由我们的其他服务器下载。(使用S3存储桶作为云源以克服源服务器的上传速度问题) - Reece

45

S3 API是否支持在单个HTTP调用中上传多个对象?

不支持,S3 PUT操作每个HTTP请求只能上传一个对象。

您可以在需要与远程存储桶同步的计算机上安装S3工具,并运行以下命令:

s3cmd sync localdirectory s3://bucket/

然后,您可以将此命令放在脚本中并创建一个定时作业以每晚运行此命令。

这应该可以实现您想要的功能。

该工具基于MD5哈希和文件大小执行文件同步,因此冲突应该很少见(如果您确实希望如此,可以使用“ s3cmd put”命令强制盲目覆盖目标存储桶中的对象)。

编辑:还请确保阅读我链接的S3工具网站上的文档 - 根据您是否想要从存储桶中删除本地已删除的文件或忽略等情况,需要不同的标志。


41
这种方法仍然使用单独的放置操作,与其他方法相比并没有本质上更快。答案已被接受,但似乎你所做的只是指向了一个可以在代码中完成同样操作的工具。 - bridiver
你也可以从节点API同步 - 查看Node S3客户端。 - Nick Licata
5
您的问题是关于5年后一次只能上传一个对象的答案是否仍然有效。 - SHIKHAR SINGH
我同意 @bridiver。 - TheLebDev
我大约有35万张图片,总共45GB的数据... 我能使用“同步”来传输它们吗? - Sotiris Kaniras
显示剩余2条评论

12

调查

是否可以将文件批量上传到Amazon S3?

是的*

S3 API支持在单个HTTP调用中上传多个对象吗?

不支持。

说明

Amazon S3 API不支持批量上传,但是awscli支持并发(并行)上传。从客户端的角度和带宽效率上看,这些选项应该表现得差不多。

 ────────────────────── time ────────────────────►

    1. Serial
 ------------------
   POST /resource
 ────────────────► POST /resource
   payload_1     └───────────────► POST /resource
                   payload_2     └───────────────►
                                   payload_3
    2. Bulk
 ------------------
   POST /bulk
 ┌────────────┐
 │resources:  │
 │- payload_1 │
 │- payload_2 ├──►
 │- payload_3 │
 └────────────┘

    3. Concurrent
 ------------------
   POST /resource
 ────────────────►
   payload_1

   POST /resource
 ────────────────►
   payload_2

   POST /resource
 ────────────────►
   payload_3

AWS命令行界面

文档提供了关于如何提高Amazon S3同步命令的传输性能的建议,其中之一是:

为了潜在地提高性能,您可以修改max_concurrent_requests的值。该值设置同时可发送到Amazon S3的请求数。默认值为10,您可以将其增加到更高的值。但是,请注意以下几点:

  • 运行更多的线程会消耗您机器上的更多资源。必须确保您的计算机有足够的资源来支持您想要的最大并发请求数。
  • 过多的并发请求可能会使系统不堪重负,从而导致连接超时或系统响应速度变慢。为避免AWS CLI的超时问题,您可以尝试将--cli-read-timeout值或--cli-connect-timeout值设置为0。

一个设置max_concurrent_requests并上传目录的脚本可能如下所示:

aws configure set s3.max_concurrent_requests 64
aws s3 cp local_path_from s3://remote_path_to --recursive

为了说明运行更多的线程会消耗更多的资源,我在一个运行aws-cli容器中(使用procpath)进行了小型测量,通过上传包含约550个HTML文件(总共约40 MiB,平均文件大小约为72 KiB)的目录到S3。下面的图表显示了上传aws进程的CPU使用率、RSS和线程数。

aws s3 cp --recursive, max_concurrent_requests=64


你使用的容器的硬件规格是什么? - No_name

3

2

这里有一个全面的批处理解决方案,使用单个CommandPool::batch调用将文件从一个文件夹复制到另一个文件夹,尽管在底层它为每个文件运行executeAsync命令,因此不确定它是否算作单个API调用。

据我所知,您应该能够使用此方法复制数十万个文件,因为没有办法将批处理发送到AWS进行处理,但如果您将其托管在AWS实例上甚至在Lambda上运行,则其“技术上”在AWS上处理。

安装SDK:

composer require aws/aws-sdk-php

use Aws\ResultInterface;
use Aws\S3\S3Client;
use Aws\S3\Exception\S3Exception;
use Aws\S3\Exception\DeleteMultipleObjectsException;

$bucket = 'my-bucket-name';

// Setup your credentials in the .aws folder
// See: https://docs.aws.amazon.com/sdk-for-php/v3/developer-guide/guide_credentials_profiles.html
$s3 = new S3Client([
    'profile' => 'default',
    'region'  => 'us-east-2',
    'version' => 'latest'
]);

// Get all files in S3
$files = array();
try {
    $results = $s3->getPaginator('ListObjects', [
        'Bucket' => $bucket,
        'Prefix' => 'existing-folder' // Folder within bucket, or remove this to get all files in the bucket
    ]);

    foreach ($results as $result) {
        foreach ($result['Contents'] as $object) {
            $files[] = $object['Key'];
        }
    }
} catch (AwsException $e) {
    error_log($e->getMessage());
}

if(count($files) > 0){
    // Perform a batch of CopyObject operations.
    $batch = [];
    foreach ($files as $file) {
        $batch[] = $s3->getCommand('CopyObject', array(
            'Bucket'     => $bucket,
            'Key'        => str_replace('existing-folder/', 'new-folder/', $file),
            'CopySource' => $bucket . '/' . $file,
        ));
    }

    try {
        $results = CommandPool::batch($s3, $batch);

        // Check if all files were copied in order to safely delete the old directory
        $count = 0;
        foreach($results as $result) {
            if ($result instanceof ResultInterface) {
                $count++;
            }
            if ($result instanceof AwsException) {
            }
        }

        if($count === count($files)){
            // Delete old directory
            try {
                $s3->deleteMatchingObjects(
                    $bucket, // Bucket
                    'existing-folder' // Prefix, folder within bucket, as indicated above
                );
            } catch (DeleteMultipleObjectsException $exception) {
                return false;
            }

            return true;
        }

        return false;

    } catch (AwsException $e) {
        return $e->getMessage();
    }
}

1

如果你想使用Java程序来实现,可以这样做:

public  void uploadFolder(String bucket, String path, boolean includeSubDirectories) {
    File dir = new File(path);
    MultipleFileUpload upload = transferManager.uploadDirectory(bucket, "", dir, includeSubDirectories);
    try {
        upload.waitForCompletion();
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}

创建s3client和传输管理器以连接到本地S3,如果您希望进行测试,请按以下方式进行:
    AWSCredentials credentials = new BasicAWSCredentials(accessKey, token);
    s3Client = new AmazonS3Client(credentials); // This is deprecated but you can create using standard beans provided by spring/aws
    s3Client.setEndpoint("http://127.0.0.1:9000");//If you wish to connect to local S3 using minio etc...
    TransferManager transferManager = TransferManagerBuilder.standard().withS3Client(s3Client).build();

0
一个文件(或文件的一部分)= 一个HTTP请求,但是Java API现在支持高效的多文件上传,而无需自己编写多线程,可以使用TransferManager

如果可能的话,TransferManager 会尝试使用多个线程同时上传单个上传的多个部分。据我所知,它不会进行批量上传。 - SHIKHAR SINGH

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接