我正在一台内存有限的机器上工作,我想以流式方式将动态生成的(不是从磁盘生成的)文件上传到S3。换句话说,当我开始上传时,我不知道文件大小,但到最后我就知道了。通常一个PUT请求有一个Content-Length报头,但也许有一种方法可以解决这个问题,比如使用多部分或块内容类型。
S3支持流上传。例如,见这里:
http://blog.odonnell.nu/posts/streaming-uploads-s3-python-and-poster/我的问题是,我可以完成同样的事情,而不必在上传开始时指定文件长度吗?
您必须通过S3的多部分API以5MiB+块上传文件。每个块都需要一个Content-Length,但可以避免将大量数据(100MiB+)加载到内存中。
- 发起S3 Multipart Upload.
- 将数据收集到缓冲区中,直到该缓冲区达到S3的较低块大小限制(5MiB)。在建立缓冲区时生成MD5校验和。
- 上传该缓冲区作为部分,存储ETag(阅读该文档)。
- 一旦你的数据达到EOF,上传最后一个块(可以小于5MiB)。
- 完成Multipart上传。
S3允许多达10,000个部件。因此,通过选择5mb的部件大小,您将能够上传高达50gb的动态文件。对于大多数用例应该足够了。
然而:如果你需要更多,你必须增加你的零件尺寸。要么使用更大的部件大小(例如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),而不会浪费不必要的内存。
关于Sean O' donnell博客链接的注释:
他的问题与你的不同-他知道并在上传之前使用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()
把答案放在这里,以防对别人有帮助:
如果你不知道你要流到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
}
}
您可以使用gof3r命令行工具来流式传输linux管道:
$ tar -czf - <my_dir/> | gof3r put --bucket <s3_bucket> --key <s3_object>
如果你正在使用Node.js,你可以使用像s3-streaming-upload这样的插件来轻松完成。
有关HTTP多部分实体请求的更多信息。您可以将文件作为数据块发送到目标。
参考: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/new-stream - | Aws s3 cp - s3://my-bucket/new-stream
我的cmd:
回声"ccc"| aws——endpoint-url=http://172.22.222.245:80——no-verify-ssl s3 cp - s3://test-bucket/ccc