Rails:使用sidekiq异步创建挂载载波上传器的记录

时间:2014-08-21 13:26:05

标签: ruby-on-rails heroku carrierwave sidekiq

我正在尝试为PostPhoto创建关联图像(Post)(创建记录以及上传和处理)并通过SideKiq将它们移动到后台进程。以下是模型:

class Post < ActiveRecord::Base
  has_many :post_photos
  accepts_nested_attributes_for :post_photos, allow_destroy: true

class PostPhoto < ActiveRecord::Base
  mount_uploader :photo, PhotoUploader
  belongs_to :post

这是我的上传者:

class PhotoUploader < CarrierWave::Uploader::Base

  include CarrierWave::RMagick
  storage :fog

  include CarrierWave::MimeTypes
  process :set_content_type

最初,在帖子保存之后,我在我的帖子控制器中运行了这个:

params[:post_photos]['photo'].each do |p|
    @post.post_photos.create!(:photo => p, :post_id => @post.id)
end

这很好用。现在我正在尝试这样做:

params[:post_photos]['photo'].each do |p|
    PostPhotoWorker.perform_async p, @post.id
end

这是我的PostPhotoWorker

class PostPhotoWorker
  include Sidekiq::Worker
  sidekiq_options queue: "high"
  sidekiq_options retry: false

  def perform(p, post_id)
    post = Post.find_by_id(post_id);
    post.post_photos.create!(:photo => p, :post_id => post_id)
  end
end

工作失败了。在sidekiq日志中,我看到了:

2014-08-21T12:23:09.583Z 24655 TID-ovhl6hlbw警告:没有将nil隐式转换为String

我知道post_id和照片临时文件p正在正确传递,post = Post.find_by_id(post_id);正在查找帖子。但似乎创造行动中的某些事情失败了。是否有一些原因导致相关照片记录的创建以及上传和处理在后台进程中失败但成功作为主要进程的一部分?作为sidekiq日志的一部分没有堆栈跟踪,所以我无法弄清楚这个问题出现在哪里。

知道造成这种情况的原因是什么?

FYI

我无法使用Carrierwave Backgrounder,因为我正在使用Heroku进行制作,并且Carrierwave Backgrounder不支持在Heroku上存储图像的背景,因为Ephemeral File System(这是我想要的主要部分)背景处理首先)。我不想做Carrierwave Direct,因为它似乎需要单独的帖子和图像形式,我还没准备好重构我的代码来支持它,至少直到我找到一种方法来做到这一点典雅。

我觉得将整个照片临时文件作为值传递给redis是一个坏主意。但是看一下redis的文档,看起来好像是should support it

1 个答案:

答案 0 :(得分:0)

首先使用carrierwave direct。它从Web客户端直接将图像上传到S3等。这可以解决文件上传的所有问题,如果它是临时文件结构,或者它如何在大文件上传时超时。

https://github.com/dwilkie/carrierwave_direct

您可以按照正常设置进行操作,但不是将详细信息保存到实际模型中,而是将上传信息放入临时属性,然后after_commit将该信息用于创建后台任务,或者在控制器中action创建后台任务。

PhotoProcessorWorker.perform_async(params[:key], @object.id)

然后是实际的逻辑。你得到了对象,你设置了上传密钥,通过执行remote_photo_url你强制下载对象,当你保存照片的处理时就会发生。

class PhotoProcessorWorker
  include Sidekiq::Worker
  sidekiq_options :queue => :default

  def perform(key, obj_id)

    obj = Object.where(:id => org_id).first
    obj.photo.key = key
    obj.remote_photo_url = obj.photo.direct_fog_url(:with_path => true)
    obj.save

  end
end

编辑:对不起雾这个FYI部分这就是你如何做多个,它需要多个请求,但它应该有效。

@post.post_photos.each do |pp|

  = direct_upload_form_for pp.photo, :remote => true, :class => "photo_upload" do |f|
    .avatar.form-group
      = f.label :photo
      = f.file_field :photo, :class => "btn btn-default has-image-preview"


#submit_all_images.button_class

有了这个,你就会有一段javascript,它会在点击#submit_all_images后提交每个表格。每次上传都会分开进行,但这样会很好,因为在前台发生的唯一工作就是后台任务的排队。

此外,如果您同时创建帖子,您可以通过将帖子表格设为远程,在成功创建后返回ID,然后在确认后以与上述相同的方式提交所有照片上传来完成此工作。