Paperclip Recipes

One of the main features in the Todobebe web sites is photo uploads. We have already improved the process a few times(refactoring FTW) and in this post I selected some recipes based in our experience:
- asynchronous upload to s3
- delaying the thumbnail generation
- anonymous photos
- saving photo dimensions
If you are using paperclip, some of the following recipes may be useful for you.
About Paperclip and Installation
Paperclip is a file upload plugin for Rails created by the thoughtbot (same guys who created shoulda and factory_girl). Paperclip is very active in github. Let it be known that it is the forth or fifth file upload plugin that I’ve already used and the best one so far IMHO.
Its installation is simple. In your config/environment.rb, add:
config.gem 'paperclip', :source => 'http://gemcutter.org'
Those using old paperclip versions may not have noticed it can be installed as a gem now (what is recommended by its developers).
Basic usage
If you just want to save a file in the file system:
class User < ActiveRecord::Base
has_attached_file :avatar, :styles => { :medium => "120x120>", :thumb => "80x80>" }
end
and for s3 storage (with a thumbnail):
class User < ActiveRecord::Base
has_attached_file :avatar, :storage => :s3,
:styles => { :medium => "120x120>", :thumb => "80x80>" },
:s3_credentials => "#{RAILS_ROOT}/config/amazon_s3.yml",
:bucket => "papertest",
:path => ":class/:id/:basename_:style.:extension"
end
That’s it! If you need to improve the users’ experience and don’t want make them wait for: (1)the thumbnail generation and (2)the upload to s3 after they already have waited (3)the upload to your server, a bit more code will be necessary.
Asynchronous upload to s3
This recipe uses the gem delayed_job which can be easily replaced by other background job solution such as workling or a rake task + a cron job. If you don’t know delayed_job, there is a nice railscast about it. Assuming you have delayed_job installed, let’s start our example:
class User < ActiveRecord::Base
has_attached_file :local_avatar, :styles => { :medium => "120x120>", :thumb => "80x80>" }
has_attached_file :remote_avatar, :storage => :s3,
:styles => { :medium => "120x120>", :thumb => "80x80>" },
:s3_credentials => "#{RAILS_ROOT}/config/amazon_s3.yml",
:bucket => "papertest",
:path => ":class/:id/:basename_:style.:extension"
end
The basic idea is to upload to the filesystem(local_avatar) and in background to upload to s3(remote_avatar). In the form there will be something like this:
<%= f.file_field :local_avatar %>
Nothing new so far. Now we’ll use a ActiveRecord callback to schedule the upload to s3:
after_save :queue_upload_to_s3 def queue_upload_to_s3 send_later(:upload_to_s3) if self.local_avatar_updated_at_changed? end def upload_to_s3 self.remote_avatar = local_avatar.to_file self.save! end
And if you don’t want to keep a local copy, just change this method:
def upload_to_s3 self.remote_avatar = local_avatar.to_file self.local_avatar = nil self.save! end
Another tip it’s to create an alias to forget that you are working with two attachments:
alias_method :avatar=, :local_avatar= def avatar self.remote_avatar? ? self.remote_avatar : self.local_avatar end
In the views you will do:
<%= f.file_field :avatar %> <%= image_tag @user.avatar.url(:thumb) %>
I really like this solution because it’s simple and non intrusive.
Alternatives:
- Delayed upload delivery to S3 with Paperclip + Delayed::Job(1 table, 2 models, temporary local files. nice solution)
- Uploading files directly to Amazon S3 using FancyUpload
Delaying the thumbnail generation
For this one I’ve made a mix of two solutions that I found:
Delaying Paperclip(broken link)- Paperclip, S3 & Delayed Job in Rails
First of all, it’s necessary to add a column (processsing, for instance) to flag that the thumbnail isn’t ready. Once you’ve done that, we have to prevent the thumbnail generation.
before_local_avatar_post_process do |image|
if image.dirty?
image.processing = true
false # halts processing
end
end
That will prevent any image processing by paperclip. Now we just need to implement a method to let the paperclip to do it and put on background, in this recipe using the delayed_job:
after_save :queue_process_styles def queue_process_styles send_later(:process_styles) if self.local_avatar.dirty? end def process_styles self.local_avatar.reprocess! self.processing = false self.save(false) end
And for placeholder image to show the thumbnails during the processing, just use the option default_url:
has_attached_file :local_avatar, :styles => { :medium => "120x120>", :thumb => "80x80>" },
:default_url => ":class/:attachment/:style/processing.png"
Alternative:
Anonymous photos
To implement it, we have to customize the file path using the options path and url:
has_attached_file :local_avatar, :url => "/system/:attachment/:id/:style/:filename", :path => ":rails_root/public:url"
The value of each of those words beginning with : is determined by a method of the Paperclip::Interpolations. The :attachment, for instance, returns the pluralized name of the attachment as given to has_attached_file. To create one is very simple:
Paperclip.interpolates :anonymous do |attachment, style|
Digest::SHA1.hexdigest("#{attachment.instance.id}")
end
Back to our example:
has_attached_file :local_avatar, :url => "/system/:attachment/:id/:style/:anonymous.:extension", :path => ":rails_root/public:url" #example: #/system/local_avatars/12/original/7b52009b64fd0a2a49e6d8a939753077792b0554.jpg
I don’t know if SHA-1 is the best way to do it and I would like to receive suggestions to improve this code.
Alternatives:
- :id
- :id_partition
- :timestamp
- an interpolation created by you
Photo dimensions
The class Paperclip::Geometry has methods to get the image dimensions. We just need to add the columns(width and height, for instance) and set their values using an appropriate callback:
before_save :set_dimensions
def set_dimensions
tempfile = self.local_avatar.queued_for_write[:original]
unless tempfile.nil?
dimensions = Paperclip::Geometry.from_file(tempfile)
self.width = dimensions.width
self.height = dimensions.height
end
end
That’s it!
Uploaded successfully!
I wanted to share the above recipes as I figure they are useful to those working in similar projects. I’d like to thank those who have worked on the projects mentioned above: Christopher Saylor, Bruno Miranda and Dante Regis.
Thank you for reading!
Photo by williac
Filed under: Ruby | 13 Comments
Tags: delayed_job, gems, paperclip, plugins, rails, Ruby, s3

Hi, great post! I wonder if you have any experience on how to handle preview of uploaded images before it is saved. For example:
I have a form that allow user to create article and at the same time user can attach a photo/image in the same form. when the user submit the form the article and the attached image will be show on a article preview page (without saving it to database). It is only saved if user click “save” on the preview page. It is easy to do preview for textfield or textarea data but I am not sure how to handle the image.
did you come across situation like this?
Thanks,
Foong
Hi Foong,
I’ve already worked with preview of uploaded images, but in my case I needed to save a temporary file and show its thumbnail in the form using AjaxUpload. Look at my solution, I think it will help you: http://gist.github.com/294561
Best,
Roberto Soares
Thanks Roberto,
It really helpful !
Thanks a lot,
Foong
Hello,
Thanks for your code snippet and it is helpful.
However,when I try out your code ( tempupload.rb),i get errors :
@uploaded_file.original_filename ( origina_filename is not a method) and
@uploaded_file.to_tempfile ( to_tempfile is not a method).
how is the tempupload.rb supposed to be used- is it a monkey patch to paperclip ??
thanks for your help.
This seems exactly what I’m looking for but I can’t figure out how to incorporate it properly. Do you have a sample app that uses this?
Thanks for a great post. I will definitely try async upload to S3 as you explained.
Cheers.
Thank you! Please feel free to contact me if you have any questions.
This is exactly what I’m looking for but have ran into a bit of a problem. The self.local_avatar.dirty? never validates to true after save. Not sure exactly whats going on here but if you have any thoughts that would be great. If I comment out that check to always send_later I end up with a constant connection to s3 that continually uploads. It will save the first file but then get stuck in a loop uploading the file to s3 with a new ID. No idea whats going on here!
Thanks!
Hi Anthony,
You are right, I’ve replaced ‘self.local_avatar.dirty?’ by ‘self.local_avatar_updated_at_changed?’ and I think it’ll work fine now. I created a repository with the solution, so you can fork and help me to improve it: http://github.com/roberto/paperclip_recipes.
Thanks!
Thanks for the reply. That should work perfectly. I’m not sure how I missed this the first time but the reason for the continuous uploading to s3 that I mentioned is because when I commented out the “self.local_avatar.dirty?” it was caught in a constant loop. It would call the “after_save :queue_upload_to_s3″ then in “upload_to_s3″ save the object again which would go right back to the after save and back to “upload_to_s3″. I’m sure this would be obvious to most but I spent a few hours trying to figure out why this was happening! It’s ok to laugh…
Thanks for great post!
hey, great bunch of recipes. made me want to keep using paperclip instead of rolling my own. i have a snag. if the above code covering preventing thumbnail generation it’s not clear where this block goes…
before_local_avatar_post_process do …
is that placed as a class method of the paperclipped model or is it part of some previously defined callback? it’s not immediately clear and i get a no method error when i place it as a class method of the paperclipped model.
thanks.