我可以在没有内容长度标头的情况下将文件上传到 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 - <my_dir/> | 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
您可以在没有 Mac 的情况下将 .ipa 文件上传到 Testflight 吗?