【问题标题】:Accelerate S3 upload with paperclip使用回形针加速 S3 上传
【发布时间】:2011-01-10 20:20:42
【问题描述】:

我正在使用回形针在 S3 中上传图像。 但我注意到这个上传速度很慢。我认为因为在完成提交之前,文件必须经过我的服务器,经过处理并发送到 S3 服务器。

有没有加速这个的方法?

谢谢

【问题讨论】:

    标签: ruby-on-rails paperclip


    【解决方案1】:

    你没有发布任何代码,所以我在这里做一些假设:

    • 在您的项目中,您有一个 AlbumImage 模型
    • Album has_many :images
    • 你已经有 paperclipaws-sdk 正确设置存储桶和其他所有内容
    • 您一次上传多张图片

    为了上传许多图片,您的表单将如下所示:

    <%= form_for @album, html: { multipart: true } do |f| %>
      <%= f.file_field :files, accept: 'image/png,image/jpeg,image/gif', multiple: true %>
    
      <%= f.submit %>
    <% end %>
    

    你的控制器看起来像这样

    class AlbumsController < ApplicationController
      def update
        @album = Album.find params[:id]
        @album.update album_params
        redirect_to @album, notice: 'Images saved'
      end
    
      def album_params
        params.require(:album).permit files: []
      end
    end
    

    为了使用您需要的相册处理图像

    class Album < ApplicationRecord
      has_many :images, dependent: :destroy
    
      accepts_nested_attributes_for :images, allow_destroy: true
    
      def files=(array = [])
        array.each do |f|
          images.create file: f
        end
      end
    end
    

    您的Image 文件将如下所示

    class Image < ApplicationRecord
      belongs_to :album
    
      has_attached_file :file, styles: { thumbnail: '500x500#' }, default_url: '/default.jpg'
    
      validates_attachment_content_type :file, content_type: /\Aimage\/.*\Z/
    end
    

    这只是重要的东西。使用此设置,上传 22 张总共 12MB 的图像需要 :files= 方法 41.1806895 秒 在我的本地服务器上平均执行。要检查方法运行所需的时间,请使用:

    def files=(array = [])
      start = Time.now
    
      array.each do |f|
        images.create file: f
      end
    
      p "ELAPSED TIME: #{Time.now - start}"
    end
    

    您要求更快地上传许多图像。有几种方法可以做到这一点。使用 jobs 将无法工作,因为您无法将图像等复杂数据传递给作业。


    请改用delayed_paperclip。它将图像样式创建(如thumbnail: '500x500#')移至后台作业。

    宝石文件

    source 'https://rubygems.org'
    
    ruby '2.3.0'
    
    ...
    gem 'delayed_paperclip'
    ...
    

    图片文件

    class Image < ApplicationRecord
      ...
      process_in_background :file
    end
    

    它加速了:files= 方法。与之前相同的上传(22 张图片,12MB)在我的机器上花费了 23.13998 秒。这比以前快 1.77963 倍。


    另一种加快速度的方法是使用Threads。从 Gemfile 和 process_in_background :file 行中删除 delayed_paperclip。更新您的 :files= 方法:

    def files=(array = [])    
      threads = []
    
      array.each do |f|
        threads << Thread.new do
          images.create file: f
        end
      end
    
      threads.each(&:join)
    end
    

    您可以试试这个,但会出现一些奇怪的错误,并且只看到保存了 4 张图像。您还必须使用Mutex。此外,您不能在线程上使用:join,因为如果您加入,该方法将等待线程完成运行。

    def files=(array = [])
      semaphore = Mutex.new
    
      array.each do |f|
        Thread.new do
          semaphore.synchronize do
            images.create file: f
          end
        end
      end
    end
    

    通过对方法的这个简单更改并且没有添加 gem,与之前相同的上传在 0.017628 秒内运行。这比 delayed_paperclip1,313 倍。它也比常规设置快 2,336 倍。


    如果你使用delayed_paperclip AND Threads 会发生什么?

    不要更改:files= 方法。只需在 Gemfile 中重新打开 delayed_paperclip 并添加回 process_in_background :file 行。

    在我的机器上使用此设置,该方法平均在 0.001277 秒内运行。那是

    • Threads13.8
    • 18,120.6 倍于delayed_paperclip
    • 比常规设置快 32,248.0

    请记住,这是在我的机器上,我还没有在生产中测试过。我也在wifi上,不是以太网。所有这些事情都会改变结果,但我认为数字不言自明。

    更快地上传图片。完成。


    更新:不要使用delayed_paperclip。它可能会导致数据库繁忙,并且某些图像可能无法保存。我已经测试过了。我认为仅使用线程就足够快了。从Image 文件中删除process_in_background 行。另外,这是我的files= 方法的样子:

    def files=(array = [])
      Thread.new do
        begin
          array.each { |f| images.create file: f }
        ensure
          ActiveRecord::Base.connection_pool.release_connection
        end
      end
    end
    

    注意:由于我们将图像保存推送到后台任务然后重定向。加载的页面上还没有图像。用户必须 refresh 更新页面。解决此问题的一种方法是使用 polling。 轮询是指 JavaScript 每 5 秒左右检查一次是否有任何更改,如果有任何更改,则对页面进行更改。

    另一种选择是使用 Web Sockets。 现在我们有了 Rails 5,我们可以使用ActionCable。每次创建图像时,我们都会广播相册的更新。如果用户在该相册的该页面上,他们将看到更新在数据库上发生时立即发生,而无需用户刷新或浏览器每 5 秒在无限循环中发出请求。

    很酷的东西。

    【讨论】:

      【解决方案2】:

      您想改善上传速度的外观还是真正加快上传速度?

      如果是前者,您可以使用 delayed_job 之类的东西将图像处理逻辑放入后台任务中。这样,当用户单击按钮时,他们会在您处理图像时立即转到他们的下一页(您可以显示“处理中”图像占位符,直到任务完成)。

      如果是后者,则完全取决于您的服务器和互联网连接。您在哪里托管?

      【讨论】:

        【解决方案3】:

        直接上传到 S3 怎么样?

        不确定回形针是否开箱即用,但你可以做到。

        http://docs.amazonwebservices.com/AmazonS3/2006-03-01/dev/index.html?UsingHTTPPOST.html

        【讨论】:

          【解决方案4】:

          使用延迟作业,这是一个很好的例子here
          或者您可以使用 Flash 上传。

          【讨论】:

            【解决方案5】:

            如果您最终采用直接上传到 S3 的方式,从而将工作从 Rails 服务器卸载,请查看我的示例项目:

            使用 Rails 3、Flash 和基于 MooTools 的 FancyUploader 直接上传到 S3 的示例项目:https://github.com/iwasrobbed/Rails3-S3-Uploader-FancyUploader

            使用 Rails 3、Flash/Silverlight/GoogleGears/BrowserPlus 和基于 jQuery 的 Plupload 直接上传到 S3 的示例项目:https://github.com/iwasrobbed/Rails3-S3-Uploader-Plupload

            顺便说一句,您可以使用 Paperclip 进行后期处理,使用类似这篇博文所述的内容:

            http://www.railstoolkit.com/posts/fancyupload-amazon-s3-uploader-with-paperclip

            【讨论】:

              【解决方案6】:

              按照 cwninja 的建议,我们直接上传到 s3 以摆脱这种额外的上传。我们使用此博客文章中描述的插件的修改版本:

              http://elctech.wpengine.com/2009/02/updates-on-rails-s3-flash-upload-plugin/

              我们的被修改为处理多个文件上传(重写了 flex 对象

              不确定它与回形针的效果如何,我们使用 attachment_fu,但让它与它一起工作还不错。

              【讨论】:

              猜你喜欢
              • 2018-04-08
              • 2012-10-08
              • 2014-10-13
              • 1970-01-01
              • 2015-01-23
              • 2014-09-09
              • 2013-09-09
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多