我可以在没有内容长度标头的情况下将文件上传到 S3 吗?

Posted

技术标签:

【中文标题】我可以在没有内容长度标头的情况下将文件上传到 S3 吗?【英文标题】:Can I stream a file upload to S3 without a content-length header? 【发布时间】:2012-01-29 00:04:12 【问题描述】:

我正在使用内存有限的机器,我想以流式方式将动态生成的(非磁盘)文件上传到 S3。换句话说,我开始上传时不知道文件大小,但到最后我会知道。通常一个 PUT 请求有一个 Content-Length 标头,但也许有办法解决这个问题,例如使用 multipart 或 chunked content-type。

S3 可以支持流式上传。例如,请看这里:

http://blog.odonnell.nu/posts/streaming-uploads-s3-python-and-poster/

我的问题是,我可以完成同样的事情而不必在上传开始时指定文件长度吗?

【问题讨论】:

smart_open Python 库会为您做到这一点(流式读写)。 【参考方案1】:

您必须通过S3's multipart API 以 5MiB+ 的块上传文件。这些块中的每一个都需要一个 Content-Length,但您可以避免将大量数据 (100MiB+) 加载到内存中。

启动 S3分段上传。 将数据收集到缓冲区中,直到该缓冲区达到 S3 的块大小下限 (5MiB)。在构建缓冲区时生成 MD5 校验和。 将该缓冲区作为 Part 上传,存储 ETag(阅读该缓冲区的文档)。 一旦数据达到 EOF,请上传最后一个块(可以小于 5MiB)。 完成分段上传。

S3 最多允许 10,000 个零件。因此,通过选择 5MiB 的部分大小,您将能够上传高达 50GiB 的动态文件。对于大多数用例来说应该足够了。

但是:如果您需要更多,则必须增加零件尺寸。通过使用更高的部分大小(例如 10MiB)或在上传期间增加它。

First 25 parts:   5MiB (total:  125MiB)
Next 25 parts:   10MiB (total:  375MiB)
Next 25 parts:   25MiB (total:    1GiB)
Next 25 parts:   50MiB (total: 2.25GiB)
After that:     100MiB

这将允许您上传高达 1TB 的文件(S3 目前对单个文件的限制为 5TB),而不会不必要地浪费内存。


关于您的 link to Sean O'Donnells blog 的注释:

他的问题与您的不同 - 他知道并在上传之前使用 Content-Length。他想改进这种情况:许多库通过将文件中的所有数据加载到内存中来处理上传。在类似这样的伪代码中:

data = File.read(file_name)
request = new S3::PutFileRequest()
request.setHeader('Content-Length', data.size)
request.setBody(data)
request.send()

他的解决方案是通过文件系统 API 获取 Content-Length。然后,他将数据从磁盘流式传输到请求流中。在伪代码中:

upload = new S3::PutFileRequestStream()
upload.writeHeader('Content-Length', File.getSize(file_name))
upload.flushHeader()

input = File.open(file_name, File::READONLY_FLAG)

while (data = input.read())
  input.write(data)
end

upload.flush()
upload.close()

【讨论】:

在 s3distcp github.com/libin/s3distcp/blob/master/src/main/java/com/amazon/… 中存在一个以 OutputStream 形式实现的 java 实现 我在github.com/alexmojaki/s3-stream-upload创建了一个专门用于此的开源库 你在哪里找到了 5MiB 的限制? 看起来您现在也可以将 cli 与管道一起使用 - github.com/aws/aws-cli/pull/903 @AlexHall 有任何 python 实现吗?【参考方案2】:

将此答案放在这里以供其他人使用,以防万一:

如果您不知道要流式传输到 S3 的数据的长度,可以使用 S3FileInfo 及其 OpenWrite() 方法将任意数据写入 S3。

var fileInfo = new S3FileInfo(amazonS3Client, "MyBucket", "streamed-file.txt");

using (var outputStream = fileInfo.OpenWrite())

    using (var streamWriter = new StreamWriter(outputStream))
    
        streamWriter.WriteLine("Hello world");
        // You can do as many writes as you want here
    

【讨论】:

这些类是否有 Java 等价物? 不知道“Hello world”的长度吗?如果输入是流,它会起作用吗? 在 dotnet 核心中不受支持,因为 Amazon.S3.IO api 的同步特性,每个 Microsoft。【参考方案3】:

您可以使用gof3r 命令行工具仅流式传输 linux 管道:

$ tar -czf - <my_dir/> | gof3r put --bucket <s3_bucket> --key <s3_object>

【讨论】:

有没有办法只做tar -czf - &lt;my_dir/&gt; | aws s3 --something-or-other【参考方案4】:

如果您使用的是 Node.js,则可以使用像 s3-streaming-upload 这样的插件来轻松完成此操作。

【讨论】:

【参考方案5】:

请参阅 HTTP 多部分实体请求的更多信息。您可以将文件作为数据块发送到目标。

【讨论】:

【参考方案6】:

参考:https://github.com/aws/aws-cli/pull/903

这里有一个概要: 要将流从 stdin 上传到 s3,请使用: aws s3 cp - s3://my-bucket/stream

要将 s3 对象下载为标准输出流,请使用: aws s3 cp s3://my-bucket/stream -

例如,如果我有对象 s3://my-bucket/stream,我可以运行以下命令: aws s3 cp s3://my-bucket/stream - | aws s3 cp - s3://my-bucket/new-stream

我的命令:

回显“ccc”| aws --endpoint-url=http://172.22.222.245:80 --no-verify-ssl s3 cp - s3://test-bucket/ccc

【讨论】:

以上是关于我可以在没有内容长度标头的情况下将文件上传到 S3 吗?的主要内容,如果未能解决你的问题,请参考以下文章

如何在不创建 IAM 用户的情况下将文件从 EKS 上传到 S3 存储桶?

如何在不使用 SDK 的情况下将文件从 Android 上传到 Amazon S3

用于将 svg 图像上传到 AWS S3 的内容类型

您可以在没有 Mac 的情况下将 .ipa 文件上传到 Testflight 吗?

如何在没有 XCode 的情况下将具有应用内购买的现有 .ipa 文件上传到 ituneconnect

如何在没有文件路径的情况下将文件上传到 Azure Blob 存储容器的根目录