【问题标题】:Tracking Upload Progress of File to S3 Using Ruby aws-sdk使用 Ruby aws-sdk 跟踪文件到 S3 的上传进度
【发布时间】:2012-08-18 14:55:38
【问题描述】:

首先,我知道在 SO 中有很多与此类似的问题。在过去的一周里,我读了大部分,如果不是全部的话。但我仍然无法为我完成这项工作。

我正在开发一个 Ruby on Rails 应用程序,它允许用户将 mp3 文件上传到 Amazon S3。上传本身可以完美运行,但进度条会大大改善网站上的用户体验。

我正在使用亚马逊官方提供的 aws-sdk gem。在上传过程中,我在其文档中到处寻找回调,但我找不到任何东西。

文件一次一个地直接上传到 S3,因此不需要将其加载到内存中。也不需要上传多个文件。

我认为我可能需要使用 JQuery 来完成这项工作,我对此很好。 我发现这个看起来很有希望:https://github.com/blueimp/jQuery-File-Upload 我什至试着按照这里的例子:https://github.com/ncri/s3_uploader_example

但我就是无法让它为我工作。

aws-sdk 的文档还简要描述了带有块的流式上传:

  obj.write do |buffer, bytes|
     # writing fewer than the requested number of bytes to the buffer
     # will cause write to stop yielding to the block
  end

但这几乎没有帮助。如何“写入缓冲区”?我尝试了一些总是会导致超时的直观选项。以及如何根据缓冲更新浏览器?

有没有更好或更简单的解决方案?

提前谢谢你。 我将不胜感激有关此主题的任何帮助。

【问题讨论】:

    标签: ruby-on-rails file-upload amazon-s3 progress-bar


    【解决方案1】:

    将块传递给#write 时产生的“缓冲区”对象是StringIO 的一个实例。您可以使用#write 或#

    file = File.open('/path/to/file', 'r')
    
    obj = s3.buckets['my-bucket'].objects['object-key']
    obj.write(:content_length => file.size) do |buffer, bytes|
      buffer.write(file.read(bytes))
      # you could do some interesting things here to track progress
    end
    
    file.close
    

    【讨论】:

    • 非常感谢。尽管我仍然不确定如何使用循环来实时更新页面,但它似乎正在工作。一件事:流媒体是否会大大减慢上传过程?
    • 一种选择是在其他位置(如 memcache/db/etc)跟踪进度。然后,您可以让 Web 浏览器点击一个单独的操作,以轮询来自不同操作的进度。流媒体不应减慢上传速度。您在块内执行的任何操作都会,因此请确保它们是快速操作。
    • 这种行为在 Ruby 2.0.0 中似乎存在问题,并且已被弃用(尽管我在代码中找不到弃用通知)。请参阅github.com/aws/aws-sdk-ruby/issues/192,其中 Trevor 说“块形式已被弃用。也就是说,我们确实支持 Ruby 2,我会看看为什么会失败。”
    • 我在 1.9.3 中取得了一些成功,尽管上传的总字节数有时会大于 file.size,原因我不明白。
    • @AndyTriggs 我假设您可能正在打印“字节”?这只是块大小,所以如果你正在做 5M 块,那么 bytes 变量在每次迭代中都是 5M,即使是最后一次。例如,对于一个 18M 的文件,最后会得到 5M+5M+5M+5M,即 18M 中的 20M。
    【解决方案2】:

    阅读AWS gem 的源代码后,我已经调整(或主要复制)分段上传方法,以根据已上传的块数产生当前进度

    s3 = AWS::S3.new.buckets['your_bucket']
    
    file = File.open(filepath, 'r', encoding: 'BINARY')
    file_to_upload = "#{s3_dir}/#{filename}"
    upload_progress = 0
    
    opts = {
      content_type: mime_type,
      cache_control: 'max-age=31536000',
      estimated_content_length: file.size,
    }
    
    part_size = self.compute_part_size(opts)
    
    parts_number = (file.size.to_f / part_size).ceil.to_i
    obj          = s3.objects[file_to_upload]
    
    begin
        obj.multipart_upload(opts) do |upload|
          until file.eof? do
            break if (abort_upload = upload.aborted?)
    
            upload.add_part(file.read(part_size))
            upload_progress += 1.0/parts_number
    
            # Yields the Float progress and the String filepath from the
            # current file that's being uploaded
            yield(upload_progress, upload) if block_given?
          end
        end
    end
    

    compute_part_size 方法定义为here,我已将其修改为:

    def compute_part_size options
    
      max_parts = 10000
      min_size  = 5242880 #5 MB
      estimated_size = options[:estimated_content_length]
    
      [(estimated_size.to_f / max_parts).ceil, min_size].max.to_i
    
    end
    

    此代码已在 Ruby 2.0.0p0 上测试

    【讨论】:

      猜你喜欢
      • 2021-04-20
      • 2014-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-02-11
      • 2016-04-13
      • 1970-01-01
      • 2021-07-14
      相关资源
      最近更新 更多