I wanted a project to allow attachments to be added to tasks. My requirements were that: users should be able to add multiple attachments to a task up to some maximum limit; there should be validation on the size of the attachments (combined with the number limit this should ensure I don’t run out of disk space too fast); and, that there should be some security on downloading a task’s attachments. Paperclip seems to be the popular Rails attachment at the moment and is still under active development, so I hoped to use that. However, it does not handle multiple attachments for a model. There is a plugin PaperclipPolymorph which adds multiple attachments to Paperclip, but I just couldn’t get it to meet my validation and security requirements. In the end I wrote my own solution and this article details it.
Update: This tutorial was completed using Rails 2.1.0, since then there have been a few changes, see the first comment (by Flyc) below. If you are using Rails 2.3+ you will at a minimum need to rename app/controller/appilication.rb to apps/controller/application_controller.rb
Firstly, if you don’t need multiple attachments, take a look at the Paperclip documentation and this webfellas article and you should be fine. Also, if you just need multiple attachments without any of the extra stuff, then PaperclipPolymorph will probably meet your needs. Lastly, I found this article by Brian Getting detailing how to handle multiple attachments with Attachment Fu (a Paperclip alternative). My tutorial closely follows his as I used it as a base for my investigations.
To demonstrate how I met my requirements I will use the example of creating a simple Rails app with a single model class, task, and allow that class to handle multiple attachments. You can download this project here.
Step 1 : Create the project
Here I just create a very simple Rails project called
a single model class
Task which contains only title and body
attributes. Then I install Paperclip using Git. As I don’t need any of
the image based functionality in Paperclip I haven’t installed any of
its associated images packages, see the documentation if you think you
might need them.
Step 2: Add the attachments model
Below is the migration I used to create a table for storing the
associations between model objects and their attachments. I have made it
polymorphic so it can be used with other model classes. It is called
Assets as if it is called
Attachments there is an error in some
versions of Rails. Use
Then create the attachments model class in
asset.rb. This class has a
few handy shortcut methods to get access to attachment information and a
polymorphic association to the actual attachment
To have a standard multiple attachments model like is seen in Brian
article and PaperclipPolymorph we just need to add an association to the
Task model. In
task.rb add the line:
has_many :assets, :as => :attachable, :dependent => :destroy.
Step 3: Add Validations
To ensure that there are never more than 5 attachments on a
that each attachment is less than a megabyte, add the following lines to
task.rb. Putting validations on the
Task model allows the
model to be reused by other classes if they need multiple attachments.
These limits can obviously be easily modified by changing the constants.
You will see these constants used throughout the rest of the project, so
that the control and display of validations is in a single place.
Step 4: Add Security
By default Paperclip stores attachments under the
thus they are generally available to everyone. To secure the attachments
Paperclip needs to be told to store the attachments in a different
place, and provided with a controller method to retrieve them. To store
the attachments in an
assets directory directly under the project
root, and have the
show method on the
them, then modify the association in
asset.rb to be the following:
This will require the creation of
assets_controller.rb as below. I
have just put the comment
# do security check here in the method where
you need to add any security checking as it tends to be application
specific. Likely security includes checking the user is logged in or has
a particular role or has permission to view the object which has the
Update: if you are using a web server that handles it, you can add
:x_sendfile => true on the end of the
send_file to lower memory
consumption. See the Rails documentation for more
Step 5: Setup Display
To handle the display for this simple app there is a
new page that
allows users to add attachments to a task, a
show page that displays
the attachments for task and an
edit page that allows both. To do this
assumes that the image
public/images/attachment.png is present and
that the css includes the following (I just added it to
library used by Brian
and originally by the
This assumes you are using Prototype and
Scriptaculous. It allows users to add a
number of files at once which are then stored in page and sent on an
update. Create the file
require the ability to upload files (lazily, I put it in
layouts/tasks.html.erb despite not all task views needing it).
Step 6: New Action
Firstly, to add new attachments to the scaffold
new.html.erb the form
needs to be updated to include
:multipart => true, this allows the
attachments to be sent along with the HTTP request. Then the
Pending Attachment paragraph creates the input field and
multifile.js handles attachment upload on submit.
pending_files will contain references to the attachments.
Note the use of the
Task constants for the allowable number and size
of the attachments.
In the controller the attachments need to be read in and added to the
new task. The below code script needs to be added into
app/controllers/tasks_controller.rb. Here most of the work is
offloaded to the protected
process_file_uploads method, so it can be
reused in other methods. Before the save or update of a task which may
have attachments added to it, call
process_file_uploads which just
loops through the HTTP parameters and builds any given attachments. They
will then be validated and persisted to the database with a save or
Step 7: Show Action
The show action is a little different - it doesn’t allow any attachments
to be added, instead just displaying a list of the current task
attachments. Below in
app/views/tasks/show.html.erb this is done with
app/views/tasks/_attachment.html.erb just displays some
basic information about the attachment, including a link for it to be
attachment.url. Remember from Step 4 that this will
return the url of the
show method to add a layer
of security. I have left the remove link in the partial, this will be
explained in the next step.
Step 8: Removing Attachments
_attachment.html.erb partial included a link to remove the
attachment. This requires the following
destroy method added to the
assets_controller.rb. It simply finds the attachment and deletes it.
destroy method’s view is
page. It then checks for an attachment add section (by looking for a
newfile_data id) and if it finds one updates the number of attachments
that may still be added. With this check the RJS can still be used
without error on pages that do not have a
newfile_data input nor a
Step 9: Edit Action
To complete the views, here is the
edit.html.erb which combines the
attachment adding functionality of
new with the attachment list of
Step 10: Ta-da!
And that completes the tutorial. You should now have a working task list with multiple attachments. My version of the app can be downloaded here for comparison or as a base.