使用回形针加速S3上传

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

有加速的方法吗?

谢谢

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

您是想更快地改善上传的外观还是实际加快上传速度?

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

如果是后者那么它完全归功于您的服务器和互联网连接。 你在哪里托管?

如何直接上传到S3?

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

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

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

  • 在您的项目中,您有一个AlbumImage模型
  • Album has_many :images
  • 您已经使用存储桶和其他所有设置正确设置了回形针和aws-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= method 41.1806895秒 。 要检查方法运行多长时间,请使用:

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

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


请改用delayed_pa​​perclip 。 它将图像样式创建(如thumbnail: '500x500#' )移动到后台作业中。

的Gemfile

 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个图像保存。 您还必须使用互斥锁 。 此外,您不能使用:join on threads,因为如果您加入,该方法将等待线程完成运行。

 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_paperclip快了1,313倍。 它也比常规设置快2,336倍。


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

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

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

  • Threads13.8
  • delayed_paperclip18,120.6
  • 比常规设置快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 

注意:因为我们将图像保存推送到后台任务然后重定向。 加载的页面还没有图像。 用户必须刷新才能更新页面。 解决此问题的一种方法是使用轮询 。 轮询是指JavaScript每隔5秒左右检查一次更改,并对页面进行更改。

另一种选择是使用Web套接字 。 现在我们已经有了Rails 5,我们可以使用ActionCable 。 每次创建图像时,我们都会广播相册的更新。 如果用户在该专辑的该页面上,他们将看到更新发生在数据库上,而无需用户刷新或浏览器在无限循环中每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

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

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

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

不知道这与回形针的效果如何,我们使用attachment_fu,但要使用它也不是那么糟糕。