使用Ruby将大文件上传到S3失败,出现内存不足错误,如何在块中读取和上载?

我们通过Ruby AWS SDK(v2)从Windows机器上传各种文件到S3。 我们已经使用Ruby 1.9进行了测试。 我们的代码工作正常,除非遇到大文件,抛出内存不足错误。

起初我们使用以下代码将整个文件读入内存:

:body => IO.binread(filepath), 

然后在Googling之后我们发现有很多方法可以用Ruby读取文件:

 :body => File.open(filepath, 'rb') { |io| io.read }, 

但是这段代码没有解决问题,我们找不到特定的S3(或相关)示例,它显示了如何读取文件并以块的forms传递给S3。 整个文件仍然加载到内存中,并在大文件中引发内存不足错误。

我们知道我们可以将文件拆分为块并使用AWS多部件上传上传到S3,但是如果可能的话,首选是避免这种情况(尽管如果这是唯一的方法就没问题)。

我们的代码示例如下。 以块的forms读取文件,避免内存不足错误以及上传到S3的最佳方法是什么?

 require 'aws-sdk' filepath = 'c:\path\to\some\large\file.big' bucket = 's3-bucket-name' s3key = 'some/s3/key/file.big' accesskeyid = 'ACCESSKEYID' accesskey = 'ACCESSKEYHERE' region = 'aws-region-here' s3 = Aws::S3::Client.new( :access_key_id => accesskeyid, :secret_access_key => accesskey, :region => region ) resp = s3.put_object( :bucket => bucket, :key => s3key, :body => File.open(filepath, 'rb') { |io| io.read }, ) 

请注意,我们没有达到S3 5GB的限制,例如1.5GB的文件会发生这种情况。

适用于Ruby的v2 AWS SDK, aws-sdk gem,直接通过网络支持流对象,而无需将其加载到内存中。 您的示例只需要一个小的更正来执行此操作:

 File.open(filepath, 'rb') do |file| resp = s3.put_object( :bucket => bucket, :key => s3key, :body => file ) end 

这是有效的,因为它允许SDK在文件对象上调用#read ,每次传递少量字节。 在没有第一个参数的Ruby IO对象(例如文件)上调用#read会将整个对象读入内存,并将其作为字符串返回。 这就是导致内存不足错误的原因。

也就是说, aws-sdk gem提供了另一个更有用的界面,用于将文件上传到Amazon S3。 这个替代界面自动:

  • 对大对象使用多部分API
  • 可以使用多个线程并行上传部件,提高上传速度
  • 计算客户端数据的MD5以进行服务端数据完整性检查。

一个简单的例子:

 # notice this uses Resource, not Client s3 = Aws::S3::Resource.new( :access_key_id => accesskeyid, :secret_access_key => accesskey, :region => region ) s3.bucket(bucket).object(s3key).upload_file(filepath) 

这是aws-sdk资源接口的一部分。 这里有很多有用的工具。 Client类仅提供基本APIfunction。

.put中存储桶的大小限制为5GB。

但是在s3中有“多部分”上传,您可以上传大尺寸的文件。

这些链接可能对您有所帮助: http : //docs.aws.amazon.com/AmazonS3/latest/dev/UploadingObjects.html http://docs.aws.amazon.com/AWSRubySDK/latest/AWS/S3/MultipartUpload.html