For attaching files, use Paperclip
Posted by Jon Yurek
Mar 18
Last Update: For archival reasons, I’m removing direct links to releases, etc. from here. All the info you need is at the official paperclip project page.
For some reason, file attachment is annoying. I don’t know why, and I know a lot of people have attempted to solve the problem in the past, myself included. Yet it still is. Having gotten fed up with gotchas and design decisions that we didn’t agree with, I went and wrote Paperclip on the plane to RailsConf last year. We’ve been using it here in various forms since and IMHO it’s the way to handle uploads, and finally decided that it should be released.
1 2 3 4 5 |
class User < ActiveRecord::Base has_attached_file :avatar, :styles => { :square => ["64x64#", :png], :small => "150x150>" } end |
A file is treated like any other attribute. It’s assigned like any other attribute, and it’s not saved until you call #save. It doesn’t have its own model. You can say where it’s saved on the filesystem, and what URL it’s referred to by (which means you can let Apache/nginx handle it or you can route it through the app for permissions/security). You can say what thumbnails are made, what resolution and format they are, and you can actually save cropped square thumbnails without any hassle.
1 2 3 4 5 6 7 8 9 |
class AddAvatarToUser < ActiveRecord::Migration def self.up add_column :users, :avatar_file_name, :string add_column :users, :avatar_content_type, :string add_column :users, :avatar_file_size, :integer end def self.down; ...; end end |
You don’t need mini_magick (which we’ve found may have been causing issues), you don’t need ImageScience (which, on the first image we tried to upload, failed to decode it, and it was a simple GIF), you don’t need RMagick (which has memory issues that were the impetus for mini_magick in the first place). You just need ImageMagick installed somewhere, which is as easy as yum, apt-get, or port on any system worth hosting on.
And this isn’t just for avatars and images. You can upload anything. No thumbnails are made by default, so it won’t automagically choke on your Excel docs.
Usage
In your model:
1 2 3 4 5 |
class User < ActiveRecord::Base has_attached_file :avatar, :styles => { :medium => "300x300>", :thumb => "100x100>" } end |
In your edit and new views:
1 2 3 |
<% form_for :user, :html => { :multipart => true } do |form| %> <%= form.file_field :avatar %> <% end %> |
In your controller:
1 2 3 |
def create @user = User.create( params[:user] ) end |
In your show view:
1 2 3 |
<%= image_tag @user.avatar.url %> <%= image_tag @user.avatar.url(:medium) %> <%= image_tag @user.avatar.url(:thumb) %> |
Installation
- piston import https://svn.thoughtbot.com/plugins/paperclip/trunk
It can’t be everything for everyone, but for the vast majority of cases we’ve come across, this is the right fit. Really, once you use this you’ll wonder why managing files was such a hassle.
Comments on this post
Mar 18
Jerrod said,
how does this differ from file_column (aside from being actively developed of course..)? Having never experienced a concrete need to abandon file_column in my projects and seeing attachment_fu as overkill for what should be a dead simple task, im quite interested in know what sets this apart from the (apparently old school… 2 years ago on the web IS old school right?) file_column.
Mar 18
omygawshkenas said,
Whoa . . . talk about cutting out the middlemen! Can’t wait to try it out.
Mar 18
Brendon said,
Just also wondering, do you store the files temporarily in case of a failed validation on things other than the file itself? Filecolumn does this, and thus, allows you to do nifty things like hide the file browser box if the file uploaded successfully but some other parts of the form failed. It then uses the temporary file the next time the form is submitted. This results in a potential for temp files to be orphined but that can be cleaned up with a cron or something. :)
Mar 19
Jon Yurek said,
@Jerrod: Well, first off, I didn’t even know file_column was still being used, heh.
But seriously, Paperclip has number of tangible differences. First being that you don’t have to use RMagick, which is known to have serious memory issues (according to the mini_magick link in the post, a single upload needs 100MB of RAM. Secondly, you can point your file store anywhere; you don’t need to have them in your public directory. Thirdly, there are no helpers you need to use to upload or display the images, and accessing the alternate styles is as easy as displaying the primary (it appears that you’d have to know what the path to a file_column thumbnail is to access it). Lastly, this may be small thing, but worth mentioning: Paperclip does not save the file to its final resting place until you call #save on the model. I think that’s an important detail when trying to integrate file management as seamlessly as possible with ActiveRecord. IMHO files should be no different than any other attribute from the developer’s perspective.
@Brendon: It does save them on disk, but not in a format that would survive a refresh, so you’d have to upload them. That seems like it could be a useful feature, though, so I’ll see if I can work that into future release.
Mar 19
Eric Mill said,
This has to be the best thoughtbot release post yet. To the point!
Paperclip is awesome, and I wish I’d been using it since Jon first made it. I’m entering a project now where we’re locked into using attachment_fu, with its requirement that an attachment have its own MODEL. COME ON! Simplicity is best.
The point about Paperclip not saving the file until the model is saved is important enough that I recommend editing it into the original post. Overall though, great release post Jon.
Mar 19
Steven Garcia said,
This looks truly fascinating! Definitely gonna try it out.
Just have one question. In attachment_fu it’s fairly easy to override the file directory methods. This is great for cases where you want to save an avatar inside a folder with the user’s name on it. Is this possible with paperclip?
Also, have you guys thought of setting up a google group for this?
Mar 19
Avi said,
Are you thinking of adding S3 support?
Mar 19
jetpilot said,
Hi, i installed Paperclip, but run into following issues:
1.in show view : image is not rendered, when i saw page source i see the following: src=”/avatar/4/thumb/Test.& gt;” (without “space” between & and gt) why path is like this instead of: ../Test.jpg?
2.I didn’t manage to upload GIF, i get Validation failed: Avatar could not be processed because the file does not exist., Avatar could not be processed because the file does not exist.
do i need extra validation for uploading GIF-s?
Mar 19
jetpilot said,
btw, I’m working on Windows platform
Mar 19
Jon Yurek said,
@Eric: I did mention that.
@Steven Garcia: Yes, it is, but, honestly I was saving that for another post so as not to make this too long. Suffice to say that if you take a look at Paperclip::Attachment.interpolations you should find what you need. And yes, there will likely be a Google group soon.
@Avi: Yeah. I was playing around with it in a prior version, but it wasn’t solid enough (IMHO) to release, especially since we weren’t actively using S3. But it is on the list.
@jetpilot: Wow, thanks for catching all that. The ”>” business and the poor default file storage location were definite bugs. The “file not found” stuff should also be fixed, but I don’t know for sure what caused that. GIFs shouldn’t need any other processing or validations. In fact, in my test app, even GIF animations were scaled and cropped correctly, so it was likely something else.
Mar 19
Jim Neath said,
I’m also having problems using paperclip on windows.
The images don’t seem to upload correctly. I’ve tried with and withouth thumbnails but both times the image just appears to be garbled.
Any help would be great.
Mar 19
Dan Croak said,
Congrats, Jon!
For the Shoulda-inclined, here’s a test helper for your test_helper:
Mar 19
John said,
Does it have additional functionality in geomerty like the ”!” sign (Resized then cropped thumbnails?)
Also can you hook additional transformations to the thumbnail generation like rotate and stuff easily?
I see this plugin has lots of potiential and even comes with a cool name
Mar 19
Jim Neath said,
@Jon: I’ve checked out the trunk version and I’m getting the “File does not exist” error on both png and gifs.
When I try to upload a jpg, I don’t get an error but the image that is produced seems to just be a blank image. Also when I try to delete the images it tells me that their still open by some other program.
Alas, this is on windows.
Mar 19
Ismael said,
What would be a pattern to have a model with many files? Like Product#has_many :assets
Does it support multiple file attributes or do I need to create an Asset model which uses Paperclip?
Mar 19
Jon Yurek said,
@John: The ! modifier already does something in ImageMagick (specifically, it resizes to exactly the specified resolution, without cropping) so I’m not using that, but yes. If you use # as your geometry modifier, the image will be resized to fit and cropped as necessary. There isn’t a method for other transformations, yet.
@Jim Neath: Off the top of my head, I would wonder if convert and identify are in your PATH. Also, the default path for saving files is geared towards Unix, so perhaps you need to change that. Lastly, is your ImageMagick compiled with PNG and GIF support? The packages I’ve seen do, but I don’t know if you compiled your own or not.
@Ismael: The best practice in that case would be to create an Asset model that has the attached file. You can have multiple attachments on a single model, but they all need their own set of columns, which doesn’t really allow for an arbitrary number of attachments.
Mar 19
Jim Neath said,
@Jon: convert and identify are both in my PATH. I’ve changed the default paths and that doesn’t seem to make a difference. ImageMagick has PNG and GIF support as well as I’ve used them in previous projects.
Any other suggestions?
Mar 19
Luis Lavena said,
@Jim: What is the size of the files are you trying to upload? Are you using Webrick or Mongrel with your Rails app?
I’m asking this since for smaller files Mongrel uses a StringIO to buffer a temporary file, and with bigger uploads switch to TempFile, and sometimes the “binary” option of the TempFile get lost in the process.
There is also a few issues with your
TEMPenvironment variable. Check you have it set to something likeC:\TEMPinstead of the “Documents and Settings…” crap Windows assigns to each user :-P@Jon: Nice plugin!, the over-engineered attachment_fu give me some headaches each time I need to change something on my app.
(will test it soon!) Luis
Mar 19
Jim Neath said,
@Luis: I’ve tried various files ranging for small gif icons to large jpeg files and nothing seems to work as it should, unfortunately.
I’m using mongrel on a windows box as that’s all I have at work (despite my best efforts to convince otherwise).
I’ll have a look into the TEMP directory issue tomorrow when I’m back in the office.
This plugin looks great and provides a lot of the things I wish attachment_fu provided so I’m very keen on trying this plugin out.
Mar 19
Raj Ojha said,
This plugin is so great, simple and easy to integrate, thank you!
Is there any plans to integrate something like Kropper?
http://kropper.captchr.com/
or maybe someone could maybe just shed some light on how to use kropper to crop thumbnails with paperclip?
Mar 20
Jim Neath said,
@Luis: I’ve changed my temp directory but the same error is still happening. I went through a few of my current apps and tested to see that they were uploading/resizing images as they should be and they all seem to work (small and large sizes, jpg, gif and png), although they use the RMagick gem.
I’ll have a poke around and see if I can find out what the problem is.
Mar 20
Jon Yurek said,
@Jim Neath: To be honest I haven’t done any testing under Windows. I’ll make sure to spend some time investigating that issue today.
@Raj Ojha: That’s a pretty neat cropping tool, but as of right now it wouldn’t work with Paperclip. I would like to be able to accommodate it, though.
Mar 20
Alex Podaras said,
Hello Jon, Thank you for this plugin, it was very easy to intergate with an application i am working and it works very well.
The only problem that i encountered was that i couldn’ t make the :default_url option to work(btw in documentation it is documented as :missing_url).
That is how i tried it:
ModelMaybe i am doing something wrong ?
Mar 20
Jon Yurek said,
@Alex: No you weren’t doing anything wrong, I was. There was a bug in the url/default_url generation. Also, thanks for catching the “missing_url” in the docs. I’ve tagged 2.0.2, or you can piston from trunk.
Mar 20
Raj Ojha said,
Thank you, I’m glad there’s a chance to be able to do manual thumbnail cropping in a future release or at least integrate with plugins like kropper.
I had been using this plugin before with their attachment plugin – http://peelmeagrape.net/projects/js_image_cropper but the js cropper prototype plugin hasn’t been update in a while.
So I guess there’s no way to bypass the thumbnail cropping but still create the thumbnail currently? (so it could redirect to something like that js_image_cropper plugin).
Mar 20
Eric Anderson said,
Just wanted to add my two cents on file_column being a good alternative also. I know it is not actively developed but it doesn’t need much.
The points about RMagick memory leaks are probably valid but in practice I haven’t experienced any problems with extensive use of file_column.
Also file_column does allow you to put the files in a different location.
FileColumn does require using some helpers but this is mainly just to provide support for not having to upload twice if validation fails. Also the model doesn’t know the full path since it doesn’t have any access to the request context so you have to use helpers to display the files. The helpers have never bothered me much but I welcome getting rid of them.
That I welcome another choice for this task. One thing I like about attachment_fu is the ability to upload to file system, database or S3. Any plans for that?
Mar 20
wilson said,
Does this work with amazon s3?
Mar 20
Flinn said,
Does this support ID Partioning ?
Mar 21
Yuhonas said,
For everybody who’s been having troubles using this under windows weather your getting the “File does not exist” or zero byte files written theres a few minor issues in the IO implementation in that it doesnt explicity set binary mode when writing Temp/Files which causes issues under windows The following will fix it
iostream.rbMar 21
Jon Yurek said,
I do expect to add support for S3, but probably not database columns.
Flinn: It’s easily done by adding a lambda to the interpolations hash. In fact, I just committed the equivalent of this to trunk:
You can redefine it as needed (or define others) that can partition on 2 or 1 digit at a time, or that partitions over 12 digits, or with no 0-padding, or whatever.
Mar 21
Jon Yurek said,
@Yuhonas: Thanks for that! I’ve added it to trunk.
Mar 24
Yuhonas said,
Hey Jon just checked out the trunk and i think you may have forgotten the change to the to_tempfile method in iostream.rb :)Mar 24
RSL said,
Please don’t just pass along incorrect information about RMagick’s supposed memory leaks. Of course mini-magick and ImageScience make claims that RMagick leaks because it hypes their products. Here’s a link that explains the whole problem and another link [from why himself] about the fact that sometimes we Ruby users do have to take GC into our own hands . I’m sure this oversight wasn’t intentional but I felt it needed to be corrected. While RMagick might be overkill for most simple thumbnailing needs it certainly does _not leak. Thanks.
Mar 24
RSL said,
Not sure what happened to that first link of mine but here it is again http://rubyforge.org/forum/forum.php?thread_id=1374&forum_id=1618
Mar 24
RSL said,
Textile keeps converting & => &38; instead of &. One last time with manual & there. http://rubyforge.org/forum/forum.php?thread_id=1374&forum_id=1618
Mar 24
Jon Yurek said,
@Yuhonas: Thanks for pointing that out. I’ve added that to trunk.
@RSL: To be fair, I never said RMagick leaked, just that it used huge amounts of memory, which it does.
Honestly, I find the idea of forcing the developer to manage memory very un-Ruby, even if it is possible. And if using RMagick requires that (which it seems to), then that’s a large point against it right there. I don’t want to say RMagick is bad, because it’s not. I’m just saying that it’s demonstrably not ideal for tasks like what Paperclip is trying to accomplish.
Mar 25
Bryan said,
This plugin looks slick. Would you mind making a gem to make it easier to use with Merb?
Mar 27
Peter Zingg said,
I was interested in writing a YouTubish application for shared storage of all kinds of resources, including videos. Taking the advice of others I added an rvideo-ffmpeg-flvtool2 background process (using a workling/starling queue) to convert uploaded videos into flv files, and pulling thumbnail still images from these as well. Can paperclip support multiple background/queued thumbnail conversions (and maybe other workflows) as well as the typical blocking thumbnail generation when a file is uploaded or modified?
Mar 27
Jon Yurek said,
@Bryan: I think I can get to that, yeah.
@Peter Zingg: No, it currently does not support a background/queue processing system. That’s a bit out of the scope it was originally designed for.
Mar 27
Dax Huiberts said,
This sure is an awesome plugin! Exactly as it should be, as a column in your model! I needed my own images for some models (think avatars and covers) and I didn’t find any image attachement plugin that suited my needs. So I’ve written a simple one a lot like yours, with simpler needs. I’m just sooo happy so see someone else that understands file attributes shouldn’t be a pain in the ass and don’t need to require extra models and helpers and stuff. Congrats! And you’re an inspiration to tidy up my own work a bit.
Apr 06
Benjamin Quorning said,
As mentioned in this blog post, a migration step is required to make this plugin work – I pulled some hair before realizing this… You might want to mention this requirement on http://thoughtbot.com/projects/paperclip and http://dev.thoughtbot.com/paperclip/files/README.html
Anyway, since adding the required columns to my model, the plugin has been working impeccably.
Apr 17
David Dollar said,
The following is a patch to Paperclip to allow it to be more friendly to non-standard deployment infrastructures.
Our deployment infrastructure involves not having a database.yml in place in the repository, it is copied into place by a Rake task during deployment.
Requiring ‘environment’ in the rake task associated with Paperclip causes rake to bomb out when it cascades down to requiring the database.yml
The attached patch adds a bit of error handling to the rake task and only requires ‘environment’ before it is actually needed.
http://pastie.caboo.se/182618
Sorry, comments are closed for this article.
© 2000 - 2009 by thoughtbot, inc.
written by a bushel of tiny robots
Come “ride the toad” on Hoptoad, the app error app.
Thunder Thimble: Brand monitoring for social media.
Widgetfinger: Simple content management for simple websites.
Tee-Bot, funny shirts your friends won't understand!
Umbrella Today: “It’s like totally the simplest weather report ever, Julie.”
Thoughtbot
thoughtbot is a technology consulting firm that provides web application development and design services. We focus on building modern systems, embracing good ideas and delivering elegant solutions.
Interested in learning Rails?
Sign up for our beginning or advanced training.
Archives