开发者

Moving Paperclip S3 attachments on Heroku

I'm trying to do something very simple using Heroku, Paperclip and S3 - set one model's attachment to equal another's.

Here's a custom rake task I put together:

task :migrate => :environment do
    @companies = Company.where("attachment_file_name IS NOT NULL")    
    @companies.each do |c|
        if c.attachments.where("attachment_file_name = ?", c.attachment_file_name).blank?
        # i.e. if there are no instances of Attachment that match c.attachment
            a = Attachment.new( :company_id => c.id, :name => "Default" )
            a.attachment = c.attachment
            a.save
        end
    end
end

So, I'm trying to move Company.attachment to a new instance of the new Attachment model. On my local development server, it works beautifully.

Once pushed to Heroku, I'm getting the following error pointing to the line a.attachment = c.attachment.

The specified key does not exist.

I try the operation manually for a company that has an attachment in the heroku console and I get:

TypeError: can't convert nil into String
/app/.bundle/gems/ruby/1.8/gems/paperclip-2.3.6/lib/paperclip/storage/s3.rb:131:in `extname'
/app/.bundle/gems/ruby/1.8/g开发者_如何学Goems/paperclip-2.3.6/lib/paperclip/storage/s3.rb:131:in `to_file'
/app/.bundle/gems/ruby/1.8/gems/paperclip-2.3.6/lib/paperclip/attachment.rb:81:in `assign'
/app/vendor/plugins/paperclip/lib/paperclip.rb:245:in `attachment='

Do you know what's going on here?


I just tried c.attachment = c.attachment. Same error!!!


Looks like c.attachment_file_name is coming up nil and paperclip doesn't know what to do with it. I'm not sure why it's nil but to get around it you can just check to see if it's nill and skip it if it is:

if c.attachment_file_name
    if c.attachments.where("attachment_file_name = ?", c.attachment_file_name).blank?
        # i.e. if there are no instances of Attachment that match c.attachment
        a = Attachment.new( :company_id => c.id, :name => "Default" )
        a.attachment = c.attachment
        a.save
    end
end


Have you considered modifying your paperclip model to accept a URL as an attachment? That way you can port over your attachements to a new model and not radically modify the paperclip s3 storage mechansim.

Add this to your new model:

before_validation :download_remote_attachment, :if => :attachment_url_provided?

...

attr_accessor :attachment_url

  private

  def attachment_url_provided?
    !self.attachment_url.blank?
  end

  def download_remote_attachment
    self.file = do_download_remote_image
  end

  def do_download_remote_attachment
    io = open(attachment_url)
    def io.original_filename; base_uri.path.split('/').last; end
    io.original_filename.blank? ? nil : io
  rescue
  end   

Then to create a new attachment object, pass it the parameter :attachment_url and it will download it, re-process it, and store it as an attachment for the new model. The only downside to this is that the attachment will be stored twice on S3. Depending on your app requirements thought that might be a good thing


One more long shot in the dark here, from the "why does it work with development db and not production db" angle. Any chance attachment is made available via a has_many :through relationship? Have read this type of bizarre error can occur with mySQL if the join table has a primary key added to it. Will work with sqLite3 though. So, once you go to production, the error is seen. Just a thought.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜