是否可以在没有内容长度标题的情况下将文件上传到S3?


70

我正在使用内存有限的计算机上工作,我想以流方式将动态生成的(非磁盘)文件上传到S3。换句话说,开始上传时我不知道文件大小,但最后我会知道。通常,PUT请求具有Content-Length标头,但也许可以解决此问题,例如使用多部分或分块的内容类型。

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

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

我的问题是,在上传开始时无需指定文件长度即可完成相同的事情吗?


smart_open Python库会替你(流读取和写入)。
Radim

Answers:


76

您必须通过S3的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),而不会浪费内存。


关于指向Sean O'Donnells博客的链接的注释:

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

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

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

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()

1
在一个OutputStream形式的Java实现的这在s3distcp存在github.com/libin/s3distcp/blob/master/src/main/java/com/amazon/...
sigget


1
您在哪里找到5MiB限制?
Landon Kuhn

1
看起来您现在也可以将cli与管道一起使用-github.com/aws/aws-cli/pull/903
chrismarx

@AlexHall任何python实现吗?
Tushar Kolhe'5

8

如果有帮助,请将此答案放在此处以供其他使用:

如果您不知道要流到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
    }
}

1
是否有这些类的Java等效项?
史蒂夫·K

“ Hello world”的长度不知道吗?如果输入是流,它可以工作吗?
at0mzk

由于Microsoft.Amazon.S3.IO api的同步特性,dotnet核心不支持此功能。
xiaochuanQ

5

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

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

有办法做tar -czf - <my_dir/> | aws s3 --something-or-other吗?


1

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.