I just updated the View Mapper gem to support Paperclip. You can use it to generate scaffolding code that supports uploading and downloading Paperclip file attachments.
Creating a view for an existing model
If you have a model like this:
class Song < ActiveRecord::Base
has_attached_file :mp3
end
… you can generate a “Paperclip view” for this model like this:
script/generate view_for song --view paperclip
This will generate a controller, view and other code files that support uploading and downloading files. If you run your app you’ll see the typical scaffolding user interface but with a file field for the “mp3” Paperclip attachment:

View Mapper has:
- inspected your model (“Song” in this example) and found its Paperclip attachments and other standard ActiveRecord attributes
- called the Rails scaffold generator and passed in the ActiveRecord columns it found
- added additional view code to support Paperclip (e.g. set the form to use multipart/form-data encoding)
- created a file field in the form for each Paperclip attachment (“mp3” in this example), as well as a link to each attachment in the show view code file.
If you’re not very familiar with Paperclip and how to use it or if you just want to get a Rails upload form working very quickly, then View Mapper can help you.
Creating an entirely new Paperclip model and view
View Mapper also provides a generator called “scaffold_for_view” that is identical to the standard Rails scaffold generator, except it will create the specified view. As an example, let’s create a new Rails app from scratch that uses Paperclip; you should be able to type in these precise commands on your machine and get this example to work.
First, let’s install View Mapper and create a new Rails app to display my MP3 library online (ignoring copyright issues for now):
$ gem sources -a http://gemcutter.org
http://gemcutter.org added to sources
$ sudo gem install view_mapper
Successfully installed view_mapper-0.2.0
1 gem installed
Installing ri documentation for view_mapper-0.2.0...
Installing RDoc documentation for view_mapper-0.2.0...
$ rails music
create
create app/controllers
create app/helpers
create app/models
create app/views/layouts
etc…
And now we can generate a new “Song” model that has a Paperclip attachment called “MP3” using View Mapper like this:
$ cd music
$ ./script/generate scaffold_for_view song name:string artist:string
album:string play_count:integer --view paperclip:mp3
error The Paperclip plugin does not appear to be installed.
Wait… I forgot to install Paperclip; let’s do that and then try again:
$ ./script/plugin install git://github.com/thoughtbot/paperclip.git
Initialized empty Git repository in /Users/pat/rails-apps/music/vendor/plugins/paperclip/.git/
remote: Counting objects: 71, done.
remote: Compressing objects: 100% (59/59), done.
remote: Total 71 (delta 7), reused 29 (delta 3)
Unpacking objects: 100% (71/71), done.
From git://github.com/thoughtbot/paperclip
* branch HEAD -> FETCH_HEAD
$ ./script/generate scaffold_for_view song name:string artist:string
album:string play_count:integer --view paperclip:mp3
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/songs
exists app/views/layouts/
exists test/functional/
etc…
Finally, we just need to run db:migrate – one minor detail here is that the scaffold_for_view generator included the Paperclip columns (“mp3_file_name,” “mp3_content_type,” etc…) in the migration file to create the songs table:
$ rake db:migrate
(in /Users/pat/rails-apps/music)
== CreateSongs: migrating ====================================================
-- create_table(:songs)
-> 0.0022s
== CreateSongs: migrated (0.0024s) ===========================================
Now you can run your app and see the scaffolding UI I showed above, and will be able to upload and download MP3 files using Paperclip.
Let’s take a quick look at exactly what is different about the scaffolding code View Mapper generated vs. the standard Rails scaffolding code:
<h1>New song</h1>
<% form_for(@song, :html => { :multipart => true }) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
… etc …
<p>
<%= f.label :mp3 %><br />
<%= f.file_field :mp3 %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', songs_path %>
The code in bold was generated by View Mapper specifically to support Paperclip since we used the “--view paperclip” command line option. You can see that “:html => { :multipart => :true }” was added to form_for to allow for file uploads, and also a file_field was added for the mp3 Paperclip attachment.
If you take a look at the show view, you’ll see:
<p>
<b>Name:</b>
<%=h @song.name %>
</p>
… etc …
<p>
<b>Mp3:</b>
<%= link_to @song.mp3_file_name, @song.mp3.url %><br>
</p>
<%= link_to 'Edit', edit_song_path(@song) %> |
<%= link_to 'Back', songs_path %>
Here a link to the file attachment was added, using Paperclip to provide the name and URL of the attachment.
Next I’ll be adding support for nested attributes and complex forms to View Mapper.
Tags:paperclip·rails·view_mapper
In part 1 of this series, I showed how to create a simple Rails web site that uses the Paperclip plugin from Thoughtbot to upload and display image files. Then in part 2, I went on to change the sample app to download the image files through a Rails controller and not just through a direct call to Apache. This would be useful if you wanted to implement security for file attachments or for a variety of other reasons.
This time I’d like to show how to modify the same sample application to save the file attachments in a database BLOB column, instead of on your web server’s file system. To jump ahead and just get the working code, look at the “part3” folder in the github repo: http://github.com/patshaughnessy/paperclip-sample-app.
But before we actually work on the sample app, a disclaimer: Don’t try this at home! Serving file content directly from the file system via Apache or some other web server will always be faster and simpler than loading the file attachments from a database table… Apache and other web servers were designed to load and serve files quickly, and there’s normally no need to issue an expensive SQL query or to make another network connection to a database server just to send files to a web browser.
So why in the world would you ever want to pay the extra performance penalty and move the files into a database table? Here are a couple of reasons:
- Your client or employer wants you to. Some companies insist on using a commercial, “enterprise” RDMS system to save file contents for one reason or another. My employer, for example, has many years of experience using Oracle and is comfortable managing large numbers of files that are stored in an Oracle table, while the thought of managing, replicating, backing up, etc., files that are simply saved on a Linux file system seems much more complex and unfamiliar.
- Security. If you need to encrypt the contents of super-secret file attachments, storing them in a database might be an easier solution if you’re willing to spend money on a database server like Oracle. And database encryption aside, generally information in a database server can be more easily protected and audited than files on your web server's file system can be.
Anyway, let’s move on and actually change the sample app to save the files in a BLOB column. The first thing we will need to do is to use the version of Paperclip that I modified; the actual Paperclip plugin from Thoughtbot does not support storing files in a BLOB column. I added some code to Paperclip – a new “storage module” – to make this possible. See my code changes to learn more.
So let’s delete the original plugin and install my version:
$ cd /path/to/paperclip-sample-app
$ rm -rf vendor/plugins/paperclip
$ ./script/plugin install git://github.com/patshaughnessy/paperclip.git
Now that we have the modified plugin installed, let’s go ahead and create the BLOB columns that we will use to save the files. I tried to design the database storage module to be easy to use; one of the decisions I made was around what these BLOB columns should be called. I decided by default to use “[attachment]_file” as the name for the primary file attachment, and “[attachment]_[style]_file” for the other styles. If you want to use other column names, you just need to specify the names in the call to “has_attached_file” in the model. See my usage post for more info.
For this sample app I’ll go ahead and use the default column names: “avatar_file,” “avatar_small_file” and “avatar_thumb_file.” Here’s how to create those columns for a MySQL database. First create a new migration as usual:
$ ./script/generate migration add_attachments_blob_avatar_to_user
exists db/migrate
create db/migrate/20090528173400_add_attachments_blob_avatar_to_user.rb
… and then edit the new migration file and add the bolded code to it:
class AddAttachmentsBlobAvatarToUser < ActiveRecord::Migration
def self.up
execute 'ALTER TABLE users ADD COLUMN avatar_file LONGBLOB'
execute 'ALTER TABLE users ADD COLUMN avatar_small_file LONGBLOB'
execute 'ALTER TABLE users ADD COLUMN avatar_thumb_file LONGBLOB'
end
def self.down
remove_column :users, :avatar_file
remove_column :users, :avatar_small_file
remove_column :users, :avatar_thumb_file
end
end
Normally to create a BLOB column you would use “add_column :users, :avatar_file, :binary.” This would work fine for Oracle and other database servers. MySQL, however, supports four different types of BLOBs: TINYBLOB (256 bytes), BLOB (64k bytes), MEDIUMBLOB (16MB) and LONGBLOB (4GB). (See http://dev.mysql.com/doc/refman/5.0/en/storage-requirements.html for more information.) If we used the Rails migrations :binary column type, then we would get normal BLOBs and have an upper limit for the file attachment size of 64k, which is not normally enough. Unfortunately, there’s no way to specify LONGBLOB for MySQL using Rails migrations, so you have to use the “execute” migration and write an actual SQL statement to add the column.
Next go ahead and run your migrations and create the new columns:
$ rake db:migrate
(in /path/to/paperclip-sample-app)
== AddAttachmentsBlobAvatarToUser: migrating =================================
-- execute("ALTER TABLE users ADD COLUMN avatar_file LONGBLOB")
-> 0.0585s
-- execute("ALTER TABLE users ADD COLUMN avatar_small_file LONGBLOB")
-> 0.0266s
-- execute("ALTER TABLE users ADD COLUMN avatar_thumb_file LONGBLOB")
-> 0.0116s
== AddAttachmentsBlobAvatarToUser: migrated (0.0973s) ========================
Now we just need to tell Paperclip to use database storage instead of the default file system storage and we’re ready to try our app and see if it saves the files into the new BLOB columns. Add the bolded parameter to has_attached_file in the User model:
class User < ActiveRecord::Base
has_attached_file :avatar,
:storage => :database,
:styles => { :thumb => "75x75>", :small => "150x150>" },
:url => '/:class/:id/:attachment?style=:style'
end
Note that I also removed the “:path” parameter; this value would be ignored by the database storage module anyway since the files will be stored in the DB. Let’s try it out! Start up the sample app, and re-edit a user record to upload a new image file:
Select a file, click “Update” to submit the form and the file will be processed by ImageMagick, and saved into our new BLOB columns by the database storage module in Paperclip…
What? Where’s the image? It turns out that we still need to make a code change to the UsersController to download the image file from the BLOB column instead of from the file system. I’ll get to this in a moment. But first, let’s look at the console and see if the new files were saved into the database properly:
$ ./script/console
Loading development environment (Rails 2.3.2)
>> User.first
=> #<User id: 1, name: "Mickey Mouse", email: "mickey@disney.com",
created_at: "2009-05-28 17:27:00", updated_at: "2009-05-28 17:37:42",
avatar_file_name: "mickey-mouse.jpg", avatar_content_type: "image/jpeg",
avatar_file_size: 137233, avatar_updated_at: "2009-05-28 17:37:42",
avatar_file: "\377???JFIF\000\001\002\001\001h\001h\000\000\377?\021\b\002\210\001?\001\021\000\002\021\001\003\021\001\377?\204\000\001\001\001\001\001\001\001...",
avatar_small_file: "\377???JFIF\000\001\001\001\001h\001h\000\000\377?C\000\003\002\002\002\002\002\003\002\002\002\003\003\003\003\004\006\004\004\004\004\004\b\006\006\005\006...",
avatar_thumb_file: "\377???JFIF\000\001\001\001\001h\001h\000\000\377?C\000\003\002\002\002\002\002\003\002\002\002\003\003\003\003\004\006\004\004\004\004\004\b\006\006\005\006...">
Here you can see the three new BLOB columns, avatar_file, avatar_small_file and avatar_medium_file, and the first few bytes of each column’s value. In fact, if you’re a real geek you’ll notice that the first few bytes contain “JFIF,” which is probably the image file type specification (not sure)… so we know we are seeing the binary contents of the three versions of the Mickey image here. Great!
Actually, not so great: there’s a subtle problem here we need to worry about. I actually typed in a very simple ActiveRecord command, “User.first,” and it loaded all three of the image files’ contents into memory just so that I could inspect the value of the user record in the console. This was convenient now, since I was actually interested in knowing whether or not each of the files was saved properly in the DB. However, this is potentially a big performance problem in general. Imagine if the files were very large… it could take a long time for all 3 of the files to be fetched by a SQL query and returned to the Rails ActiveRecord object. Do I really want or need this to happen every time I access a User record? Usually when I load a User record it’s just because I need to check the value of one of the metadata columns, like the user’s name, email address or something else. And if I do need one of the files, why should I have to load all three files? Spending the time required to load the file contents for each image style is a big performance penalty that isn’t usually necessary.
The solution I came up for this problem was to enable Paperclip to add a method to your model class called “select_without_file_columns_for” that you can use as a named scope or, even better, a default scope. It returns a :select scope hash that will exclude the BLOB columns from the SQL query that ActiveRecord issues to load each record. If you’re using Rails 2.3 or higher, you can use select_without_file_columns_for as a default scope in your model like this:
class User < ActiveRecord::Base
has_attached_file :avatar, :storage => :database,
:styles => { :thumb => "75x75>", :small => "150x150>" },
:url => '/:class/:id/:attachment?style=:style'
default_scope select_without_file_columns_for(:avatar)
end
To learn more about default scopes and to see a couple of other examples, read this: http://ryandaigle.com/articles/2008/11/18/what-s-new-in-edge-rails-default-scoping … or http://m.onkey.org/2009/3/24/default-scopes-and-inheritance-to-the-rescue. Let’s see how this works in the console again after adding the default scope:
$ ./script/console
Loading development environment (Rails 2.3.2)
>> User.first
=> #<User id: 1, name: "Mickey Mouse", email: "mickey@disney.com",
created_at: "2009-05-28 17:27:00", updated_at: "2009-05-28 17:37:42",
avatar_file_name: "mickey-mouse.jpg", avatar_content_type: "image/jpeg",
avatar_file_size: 137233, avatar_updated_at: "2009-05-28 17:37:42">
Cool. Now the BLOB columns are not displayed. “Default scope” refers to the fact that the SQL used by ActiveRecord by default to load records is automatically changed. I didn’t have to pay the price of loading each of the files, and I was still able to load all of the other User columns using ActiveRecord the way I normally would. To see what happened, execute the “select_without_file_columns_for” method directly in the console:
>> User.select_without_file_columns_for :avatar
=> {:select=>"id,name,email,created_at,updated_at,avatar_file_name,
avatar_content_type,avatar_file_size,avatar_updated_at"}
It‘s a lot more common to use a default or named scope with :conditions (to modify the WHERE clause) or :order (to modify the ORDER BY clause) but in this case I’ve used :select to specify which columns should be loaded by ActiveRecord (the SELECT portion of the SQL). If you look at the hash, you’ll see all of the User columns listed, except for avatar_file, avatar_small_file and avatar_thumb_file.
If you’re using Rails 2.2 or earlier default scope is not available yet, and you will need to use a named scope, like this:
class User < ActiveRecord::Base
has_attached_file :avatar,
:storage => :database,
:styles => { :thumb => "75x75>", :small => "150x150>" },
:url => '/:class/:id/:attachment?style=:style'
named_scope :without_file_data, select_without_file_columns_for(:avatar)
end
And you will need to use the named scope explicitly to avoid loading the files, like this:
$ ./script/console
Loading development environment (Rails 2.3.2)
>> User.without_file_data.first
=> #<User id: 1, name: "Mickey Mouse", email: "mickey@disney.com",
created_at: "2009-05-30 12:14:03", updated_at: "2009-05-30 12:14:03",
avatar_file_name: "mickey-mouse.jpg", avatar_content_type: "image/jpeg",
avatar_file_size: 137233, avatar_updated_at: "2009-05-30 12:14:03">
A good way to understand exactly what ActiveRecord is doing is to use a Ruby trick and open up the User class and add some debug code to it. Try entering this code into your console:
>> class User < ActiveRecord::Base
>> def self.find_by_sql(sql)
>> puts "DEBUG: #{sql}"
>> super
>> end
>> end
=> nil
What this code does is open up our User model class, and override the “find_by_sql” ActiveRecord method to display the SQL statement before calling the original base class method to execute it. Find_by_sql is the method that the various different ActiveRecord find methods all call once they have constructed a SQL statement. For example, find :first, find :all, first, last… all of these eventually call find_by_sql.
Assuming we have the version of User with the named_scope, we can see what SQL statements are issued with or without the select_without_file_columns_for scope:
>> User.first
DEBUG: SELECT * FROM `users` LIMIT 1
=> #<User id: 1, name: "Mickey Mouse", email: "mickey@disney.com",
created_at: "2009-05-28 17:27:00", updated_at: "2009-05-28 17:37:42",
avatar_file_name: "mickey-mouse.jpg", avatar_content_type: "image/jpeg",
avatar_file_size: 137233, avatar_updated_at: "2009-05-28 17:37:42",
avatar_file: "\377???JFIF\000\001\002\001\001h\001h\000\000\377?\021\b\002\210\001?\001\021\000\002\021\001\003\021\001\377?\204\000\001\001\001\001\001\001\001...",
avatar_small_file: "\377???JFIF\000\001\001\001\001h\001h\000\000\377?C\000\003\002\002\002\002\002\003\002\002\002\003\003\003\003\004\006\004\004\004\004\004\b\006\006\005\006...",
avatar_thumb_file: "\377???JFIF\000\001\001\001\001h\001h\000\000\377?C\000\003\002\002\002\002\002\003\002\002\002\003\003\003\003\004\006\004\004\004\004\004\b\006\006\005\006...">
>> User.without_file_data.first
DEBUG: SELECT id,name,email,created_at,updated_at,avatar_file_name,
avatar_content_type, avatar_file_size,avatar_updated_at FROM `users` LIMIT 1
=> #<User id: 1, name: "Mickey Mouse", email: "mickey@disney.com",
created_at: "2009-05-28 17:27:00", updated_at: "2009-05-28 17:37:42",
avatar_file_name: "mickey-mouse.jpg", avatar_content_type: "image/jpeg",
avatar_file_size: 137233, avatar_updated_at: "2009-05-28 17:37:42">
Here we can see that ActiveRecord will load all of the User columns that are listed above in the hash we pass to named_scope… all of the columns except for the BLOB columns. This is different from what ActiveRecord does by default, which is a simple SELECT * FROM …. statement.
Enough about ActiveRecord internals… let’s get back to the sample app and finish it up:
So why didn’t the image appear here properly? The reason is that in UsersController I’m still using the code that accesses the file on the file system and streams it to the client using send_file (see my last post for more info):
def avatars
user = User.find(params[:id])
style = params[:style] ? params[:style] : 'original'
send_file user.avatar.path(style),
:type => user.avatar_content_type
end
Obviously send_file is no longer going to work for us. Instead we need to use a similar function in ActionController::Streaming called send_data, which takes the binary data directly as a parameter instead of a file. And to access the file’s contents, I’ve added a method called “file_contents” to Paperclip that returns the actual file contents for the given style, or for the original style by default. Here’s how to put it all together; replace the “avatars” method in UsersController with this new version instead:
def avatars
user = User.find(params[:id])
style = params[:style] ? params[:style] : 'original'
send_data user.avatar.file_contents(style),
:type => user.avatar_content_type
end
Only the line in bold has changed. We just call user.avatar.file_contents, pass in the specified style and then pass along the data to send_data.
If you restart the app and refresh your browser, now you should see the image again:
Now we are seeing the binary image file data loaded from the BLOB column by ActiveRecord and streamed down to the browser by send_data.
One last detail about this: since usually everyone will use the same controller code to load the file contents for a given style from the BLOB, and then pass it along to send_data, I enabled Paperclip to add another utility method, this time to your controller, to make this even easier:
class UsersController < ApplicationController
downloads_files_for :user, :avatar
etc…
If you call “downloads_files_for” from your controller like this and specify the model and file attachment, it will automatically generate the correct controller method for you, and call it “avatars” or whatever the plural version of your attachment name is. I chose the name to make it easy and natural to create a route to it in routes.rb. No need to even think about send_data or how to get the file contents from Paperclip at all! Nothing could be simpler. However, if you need to implement security or some other business rules around downloading files, then you might need to add that business logic to the “avatars” method above. Either way, it’s very simple.
Tags:paperclip·rails
Last time I wrote about how to quickly setup a Rails application using scaffolding that allows users to upload image files and then display them using the Paperclip plugin. Paperclip does the simplest thing possible by default: it saves the file attachments right on the file system of your web server, allowing you to download them to users easily using Apache or whatever web server you have installed.
Today I’d like to take that sample app one step further and show how to use a Rails controller to download the files, instead of directly through Apache. To get the finished code just go to http://github.com/patshaughnessy/paperclip-sample-app and look at the “part2” folder.
There are a variety of reasons why you might want to do this, including:
- Security: you don’t want to expose all of the file attachments to all users of your web site. Instead, you want to implement some business rules and show some files to some users, but not to others.
- Auditing/logging: you want to keep track of who is viewing which files, or how many times they are viewing them.
- You want to hide the actual location of the files from users, and instead map the files to URLs in some other pattern or manner.
- Or, you might want not want to store the files on your web server’s file system at all, but instead in a database table or somewhere else. In Part 3 of this series, I’ll show how to do this…
The common thread here is that you want to execute some Ruby code every time a users accesses a file, and the way to do that is by routing the download requests through a controller.
Let’s pick up where we left off last time:

If we take a look at some of the HTML source for this page:
<h1>Listing users</h1>
<table>
<tr>
<th>Photo</th>
<th>Name</th>
<th>Email</th>
</tr>
<tr>
<td><img alt="Mickey-mouse"
src="/system/avatars/1/thumb/mickey-mouse.jpg?1242395876" /></td>
<td>Mickey Mouse</td>
<td>mickey@disney.com</td>
<td><a href="/users/1">Show</a></td>
<td><a href="/users/1/edit">Edit</a></td>
… we can see that Paperclip’s “url” function which we called in index.html.erb is returning a pointer to the actual location of the file on the web server’s hard drive, under the public/system folder:
$ find public/system
public/system
public/system/avatars
public/system/avatars/1
public/system/avatars/1/original
public/system/avatars/1/original/mickey-mouse.jpg
public/system/avatars/1/small
public/system/avatars/1/small/mickey-mouse.jpg
public/system/avatars/1/thumb
public/system/avatars/1/thumb/mickey-mouse.jpg
Now let’s say that we want to implement some simple security around these images…. reason #1 from my list above. The first thing we’ll need to do, then, is to remove the image files from the public folder and instead save them in some non-public place on our web server, for example:
$ mkdir non-public
$ mv public/system non-public/.
Now let’s double check that Apache can’t find the files in their new location:

Great! We see a missing image as expected. No users can see the files unless we run some bit of Ruby code to enable access. Now… how can we use a controller to download the files through Rails, instead of through Apache? The first thing we need to do is add a route to routes.rb for accessing the files.
If you open up routes.rb and look at what the scaffolding generator created for us, you’ll see this:
ActionController::Routing::Routes.draw do |map|
map.resources :users
...etc...
map.connect ':controller/:action/:id'
map.connect ':controller/:action/:id.:format'
end
The map.resources line indicates that our application supports a series of routes that handle the four REST actions: GET, POST, PUT and DELETE. The best way to get a handle on how the routes work is by running “rake routes” to list out all the URL patterns that Rails will match on:
$ rake routes
(in /Users/pat/rails-apps/paperclip-sample-app)
users GET /users(.:format) {:action=>"index", :controller=>"users"}
POST /users(.:format) {:action=>"create", :controller=>"users"}
new_user GET /users/new(.:format) {:action=>"new", :controller=>"users"}
edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
user GET /users/:id(.:format) {:action=>"show", :controller=>"users"}
PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
DELETE /users/:id(.:format) {:action=>"destroy", :controller=>"users"}
/:controller/:action/:id
/:controller/:action/:id(.:format)
The last two lines are the “default routes,” which connect any URL matching the pattern controller/action/id to the corresponding controller. We could just go ahead and use the default routes, but to learn a bit more about how map.resources works, let’s create a new URL pattern for our users that we’ll use in a minute to download the avatar file attachments with. Edit routes.rb and add the :member parameter in bold:
ActionController::Routing::Routes.draw do |map|
map.resources :users, :member => { :avatars => :get }
etc...
If we now re-run rake routes, we can see that a new URL pattern was created for us that we can use to download the avatar images via a GET request:
$ rake routes
(in /Users/pat/rails-apps/paperclip-sample-app)
users GET /users(.:format) {:action=>"index", :controller=>"users"}
POST /users(.:format) {:action=>"create", :controller=>"users"}
new_user GET /users/new(.:format) {:action=>"new", :controller=>"users"}
edit_user GET /users/:id/edit(.:format) {:action=>"edit", :controller=>"users"}
avatars_user GET /users/:id/avatars(.:format) {
:action=>"avatars",
:controller=>"users"
}
user GET /users/:id(.:format) {:action=>"show", :controller=>"users"}
PUT /users/:id(.:format) {:action=>"update", :controller=>"users"}
DELETE /users/:id(.:format) {:action=>"destroy", :controller=>"users"}
/:controller/:action/:id
/:controller/:action/:id(.:format)
Now to get the avatar for user 7, for example, we can issue a URL like this:
http://localhost:3000/users/7/avatars
… and the request will be routed to the “avatars” action in the “users” controller (plural since a user might have more than one style of avatar). So now let’s go right ahead and implement the avatars method and add some code to download a file to the client. The way to do that is to use ActionController::Streaming::send_file. It’s simple enough; we just need to pass the file’s path to send_file as well as the MIME content type which the client uses as a clue for deciding how to display the file, and that’s it! Let’s hard code these values for now and see if it all works (update the path here for your machine):
class UsersController < ApplicationController
def avatars
send_file '/path/to/non-public/system/avatars/1/original/mickey-mouse.jpg',
:type => 'image/jpeg'
end
Now if you type http://localhost:3000/users/1/avatars into your browser you should see the mickey image again. If not, then double check your code changes, where the files are actually located on the hard drive now and also try stopping and reloading the Rails app since changes to routes.rb are cached and are only loaded when Rails is initialized.
Instead of hard coding the path in the avatars method, we obviously need to be able to handle requests for any avatar file attachment for any user record. Before we enhance our code to do this, let’s take a few minutes to configure Paperclip and tell it where the files are now stored on the file system, and which URL we have configured our routes.rb file to use. This will make our work coding in the controller a lot easier, and also indicate to Paperclip where new file attachments should be uploaded to. To do this, we need to add a couple of parameters to our call to has_attached_file in our User model (user.rb), shown here in bold (again, update the path for your machine):
class User < ActiveRecord::Base
has_attached_file :avatar,
:styles => { :thumb => "75x75>", :small => "150x150>" },
:path => '/path/to/non-public/system/avatars/1/original/mickey-mouse.jpg',
:url => 'users/1/avatars'
end
Just to take one step at a time, I’ve hard coded the URL and path again here in the model. But now we can generalize our code in UserController to handle any user, like this:
def avatars
user = User.find(params[:id])
send_file user.avatar.path, :type => user.avatar_content_type
end
Now we can test http://localhost:3000/users/1/avatars again to be sure that we haven’t broken anything. If it’s all working, lets’ proceed to clean up the hard coding in user.rb. It turns out that Paperclip uses the same interpolations idea that we saw above in routes.rb. So I can use symbols like :rails_root, :id, :style, etc., and they will be evaluated to the values I expect and need. Here’s the finished code in my model:
has_attached_file :avatar,
:styles => { :thumb => "75x75>", :small => "150x150>" },
:path =>
':rails_root/non-public/system/:attachment/:id/:style/:basename.:extension',
:url => '/:class/:id/:attachment'
If we open up the console and take a look at our user object there, we can see that Paperclip is substituting the correct values for each of the symbols I’ve provided:
$ ./script/console
Loading development environment (Rails 2.3.2)
>> User.first.avatar.url
=> "/users/1/avatars?1242395876" (time stamp appended here automatically)
>> User.first.avatar.path
=> "/Users/pat/.../non-public/system/avatars/1/original/mickey-mouse.jpg"
Now let’s go back and test our original application again with our new code:
- The new route in routes.rb
- The new action in UserController
- And the :url and :path parameters added to has_attached_file in the User model.
Oops… we see the large image again. It doesn’t work! What happened? Well, it turns our that we forgot one detail in our new avatars method in UserController. Our code always returns the default, or “original” style of the file attachment, but in the users index view we actually display the thumbnail image using image_tag user.avatar.url(:thumb). So our controller code needs to be able to handle requests for other styles as well. To do this, we need to pass the requested style somehow. The simplest thing to do is just to add the style as a URL parameter to the download request, like this:
http://localhost:3000/users/1/avatar?style=thumb
(We could also add the style to the URL's path, but that would require another change to routes.rb.) To make this work, first I need to tell Paperclip about how I want to handle the style value in the URL:
has_attached_file :avatar,
:styles => { :thumb => "75x75>", :small => "150x150>" },
:path =>
':rails_root/non-public/system/:attachment/:id/:style/:basename.:extension',
:url => '/:class/:id/avatar?style=:style'
And now I need to handle this new parameter in my controller:
def avatars
user = User.find(params[:id])
style = params[:style] ? params[:style] : 'original'
send_file user.avatar.path(style),
:type => user.avatar_content_type
end
I don’t need to change anything in index.html.erb since there we call image_tag user.avatar.url(:thumb) which picks up the new URL pattern from Paperclip.
And now, if I’ve got all of this correct, I should be able to finally see the thumbnail image again on the index page:

And finally we have files being downloaded by Ruby code present in the UserController class. If we actually wanted to implement security, logging or some other sort of logic we would just add code to the avatars method in UserController. For example, avatars could return a 401 (unauthorized) error if the user wasn’t logged in, or didn’t have access to view Mickey’s image for some reason.
That’s it for now; next time I’ll modify this sample app once more to demonstrate how we can store the image files in a database table, instead of in the “non-public” folder or anywhere on the file system.
Tags:paperclip·rails
(Update October 2009)
I just updated a gem I wrote called View Mapper that will generate all of the code I describe below… you can use View Mapper to generate working scaffolding code that uploads/downloads files using Paperclip, or only view scaffolding code that works with an existing model in your app; for more details see: http://patshaughnessy.net/2009/10/16/paperclip-scaffolding
I love scaffolding. Many experienced Rails developers scoff at the idea of using scaffolding to generate Rails code: it’s ugly; it probably means you don’t understand how to write the code yourself; it generates a lot more code than you need, etc., etc. However, for a beginning Rails developer working on her/his own like me who isn’t surrounded by a team of Ruby experts, scaffolding is an essential tool and can help to get started in the right direction. Also, even for experienced Rubyists scaffolding can be a great way to quickly (minutes, not hours or days) get a simple app up and running to use for demos, UI wireframes, spiking some technical issue, etc.
This post will demonstrate how to use scaffolding to create a new Rails app from scratch that uses the Paperclip plugin to upload and display an image file. Feel free to copy/paste pieces of code from the narrative below and use them in your app, or you can just skip to the chase and get the finished version from github and run that on your machine.
There are a lot of other good tutorials out there about this; see:
I’ll take on the risk of repeating material that’s already out there in order to show how easy it is to get a working Paperclip application up and running using scaffolding. The fact that just a few commands and lines of code are required illustrates just how simple and powerful Paperclip’s design is. In my next post, I’ll proceed to change this sample app to demonstrate how to save the uploaded files in a database column instead of on the web server’s file system, using my modified version of Paperclip.
FYI At the time I wrote this, Rails was at version 2.3.2:
$ rails --version
Rails 2.3.2
Let’s get started by creating a new Rails application:
$ rails paperclip-sample-app
create
create app/controllers
create app/helpers
create app/models
create app/views/layouts
create config/environments
create config/initializers
create config/locales
create db
create doc
create lib
create lib/tasks
create log
etc...
Before we go any farther, let’s setup our database.yml file and create a new MySQL database to use with the sample app. Replace the contents of config/database.yml with this:
development:
adapter: mysql
database: paperclip_sample_app_development
username: root
password:
host: localhost
Enter the proper username and password for MySQL if they are not “root” and null. And then run this from the command line:
$ cd paperclip-sample-app
$ rake db:create
(in /Users/pat/rails-apps/paperclip-sample-app)
Ok, now we have a MySQL database to work with. Next, let’s go ahead and install the Paperclip plugin. The best thing to do is just to get the latest version from github; Thoughtbot frequently updates it with bug fixes, enhancements, etc.:
$ ./script/plugin install git://github.com/thoughtbot/paperclip.git
Initialized empty Git repository in /Users/pat/rails-apps/paperclip-sample-app/vendor/plugins/paperclip/.git/
remote: Counting objects: 62, done.
remote: Compressing objects: 100% (50/50), done.
remote: Total 62 (delta 6), reused 39 (delta 4)
Unpacking objects: 100% (62/62), done.
From git://github.com/thoughtbot/paperclip
* branch HEAD -> FETCH_HEAD
Now that we have an empty, shell application created and the Paperclip plugin installed, we can use scaffolding to add some working code to it. Let’s use the same “user” and “avatar” example Thoughtbot does on the Paperclip project page. The idea is that the sample will contain a table of users, and each user will have an avatar image displayed in the web site. So to get started, I’ll just create a new “user” model with string columns for the name and email address:
$ ./script/generate scaffold user name:string email:string
exists app/models/
exists app/controllers/
exists app/helpers/
create app/views/users
exists app/views/layouts/
exists test/functional/
exists test/unit/
create test/unit/helpers/
exists public/stylesheets/
create app/views/users/index.html.erb
create app/views/users/show.html.erb
etc...
Now we need to generate the database columns necessary for Paperclip on our new model object using script/generate:
$ ./script/generate paperclip user avatar
exists db/migrate
create db/migrate/20090430084151_add_attachments_avatar_to_user.rb
And let’s go ahead and create the users table using db:migrate:
$ rake db:migrate
(in /Users/pat/rails-apps/paperclip-sample-app)
== CreateUsers: migrating ====================================================
-- create_table(:users)
-> 0.0031s
== CreateUsers: migrated (0.0032s) ===========================================
== AddAttachmentsAvatarToUser: migrating =====================================
-- add_column(:users, :avatar_file_name, :string)
-> 0.0063s
-- add_column(:users, :avatar_content_type, :string)
-> 0.0069s
-- add_column(:users, :avatar_file_size, :integer)
-> 0.0085s
-- add_column(:users, :avatar_updated_at, :datetime)
-> 0.0081s
== AddAttachmentsAvatarToUser: migrated (0.0311s) ============================
You can see that the Paperclip generator created columns in the users table called “avatar_file_name,” “avatar_content_type,” “avatar_file_size” and “avatar_updated_at.” Now we have our database schema setup. The next step is to just modify the code that was generated for us by the scaffolding and make the changes necessary for Paperclip. The first thing to do is to add a line to the user model and indicate that it has a file attachment called “avatar.” To do this, open app/models/user.rb and just add this one line:
class User < ActiveRecord::Base
has_attached_file :avatar
end
And then edit the new user form (app/views/users/new.html.erb) and add a file field to use to upload files. There are actually two code changes you need to make: first you need to set the HTML form to encode the uploaded file data (and other fields) using MIME multiple part syntax, and then second you need to actually add the file upload field. Here’s the finished new.html.erb file with these two changes in bold:
<h1>New user</h1>
<% form_for(@user, :html => { :multipart => true }) do |f| %>
<%= f.error_messages %>
<p>
<%= f.label :name %><br />
<%= f.text_field :name %>
</p>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :avatar %><br />
<%= f.file_field :avatar %>
</p>
<p>
<%= f.submit 'Create' %>
</p>
<% end %>
<%= link_to 'Back', users_path %>
Also make the same changes to the edit form that was generated by the scaffolding: app/views/users/edit.html.erb. The best thing to do would be to include the same ERB file (maybe called “_form.html.erb”) in both the new and edit form files. Ideally the scaffolding generator would have done this for us…
Now if we run our application we can upload an image file and attach it to a user:

If you submit this form, the image file will be uploaded to the server and saved on the file system. By default, Paperclip saves files inside a “system” folder it creates in your Rails app’s public folder. Let’s take a look at my public folder and see where the file went:
$ find public/system
public/system
public/system/avatars
public/system/avatars/1
public/system/avatars/1/original
public/system/avatars/1/original/mickey-mouse.jpg
This is one of the nice things about Paperclip: it just works. I don’t have to think about or worry about where the files are going to go; Thoughtbot has chosen simple default values that make sense. Here we can see that there are a series of folders created that correspond to the attachment name, model primary key and also the “style” of the attachment (more on that below).
If you want to or need to save the files in some other place on your server’s file system you can specify different options to has_attached_file in your model; see this write up for an example: http://travisonrails.com/2009/01/11/Changing-Paperclip-File-Storage-Location. Paperclip also supports saving the files in Amazon’s S3 storage service, and in my next post I’ll demonstrate how to save the file data inside the database itself, right in the users table in this example.
I’m almost done; now I just need to display the uploaded image somewhere; the simplest thing to do is just to add an image tag to the users show page. Again, my changes to the standard scaffolding code are in bold:
<p>
<b>Name:</b>
<%=h @user.name %>
</p>
<p>
<b>Email:</b>
<%=h @user.email %>
</p>
<p>
<b>Avatar:</b>
<%= image_tag @user.avatar.url %>
</p>
<%= link_to 'Edit', edit_user_path(@user) %> |
<%= link_to 'Back', users_path %>
Now we can see the image for our new user:

Since this image is bigger that what I would like, I can take advantage of Paperclip “styles” feature to generate a smaller version of it. To do that you will need to be sure you have ImageMagick installed on your server, which is what Paperclip uses behind the scenes to modify image files. Then all you need to do is just add two “styles” to your model, like this:
class User < ActiveRecord::Base
has_attached_file :avatar,
:styles => {
:thumb => "75x75>",
:small => "150x150>"
}
end
The strings we pass in are actually options for ImageMagick's "convert" command; see it’s documentation for more details. And now in the show ERB we can just specify the “small” style in the image tag instead:
<%= image_tag @user.avatar.url(:small) %>
To see it, first I need to re-edit and re-upload the image (remember to add the file field code to edit.html.erb just like for new.html.erb):

Now when this form is submitted we will see the smaller image:

And let’s take another look at the public/system folder and see what Paperclip and ImageMagick have done for us:
$ find public/system
public/system
public/system/avatars
public/system/avatars/1
public/system/avatars/1/original
public/system/avatars/1/original/mickey-mouse.jpg
public/system/avatars/1/small
public/system/avatars/1/small/mickey-mouse.jpg
public/system/avatars/1/thumb
public/system/avatars/1/thumb/mickey-mouse.jpg
Again, this is very simple and just works! As a last step, let’s add the thumbnail image to the users index page so we can see Mickey without even clicking on that user record. This is as simple as editing app/views/users/index.html.erb and adding a new table column:
<table>
<tr>
<th>Photo</th>
<th>Name</th>
<th>Email</th>
</tr>
<% @users.each do |user| %>
<tr>
<td><%= image_tag user.avatar.url(:thumb) %></td>
<td><%=h user.name %></td>
<td><%=h user.email %></td>
<td><%= link_to 'Show', user %></td>
<td><%= link_to 'Edit', edit_user_path(user) %></td>
<td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>
</tr>
<% end %>
</table>
And now we just need to refresh the index page since the thumb image file was already generated:

And there you have it: a working file upload web site written in minutes. This was made possible by Rails scaffolding, and Paperclip's simple, elegant design.
Tags:paperclip·rails
Back in February I wrote an implementation of a new storage module for Paperclip that supports saving file attachments in a database table. My original implementation saved the file attachments in a separate database table, which was internally managed using a “has_many” relationship from the target model.
This month I decided to rewrite the code to use a simpler, single table approach. Instead of saving the files in a separate table, each file is saved in a BLOB column in the same table as the target model. This makes the database storage module easier and more intuitive to use, and moves it closer to the original intent of the Paperclip design.
Code: http://github.com/patshaughnessy/paperclip
New usage:
- Install and use my version of the plugin:
script/plugin install git://github.com/patshaughnessy/paperclip.git
- In your model specify the "database" storage option; for example:
has_attached_file :avatar, :storage => :database
The file will be stored in a column called [attachment name]_file (e.g. "avatar_file") by default. To specify a different BLOB column name, use :column, like this:
has_attached_file :avatar,
:storage => :database,
:column => 'avatar_data'
- If you have defined different styles, these files will be stored in additional columns called [attachment name]_[style name]_file (e.g. "avatar_thumb_file") by default. To specify different column names for each style, use :column in the style definition, like this:
has_attached_file :avatar,
:storage => :database,
:styles => {
:medium => {
:geometry => "300x300>",
:column => 'medium_file'
},
:thumb => {
:geometry => "100x100>",
:column => 'thumb_file'
}
}
- You need to create these new columns in your migrations or you'll get an exception. Example:
add_column :users, :avatar_file, :binary
add_column :users, :avatar_medium_file, :binary
add_column :users, :avatar_thumb_file, :binary
Note the migration for the "binary" column type will not work for LONGBLOBs in MySQL. Here's an example migration for MySQL:
execute 'ALTER TABLE users ADD COLUMN avatar_file LONGBLOB'
execute 'ALTER TABLE users ADD COLUMN avatar_medium_file LONGBLOB'
execute 'ALTER TABLE users ADD COLUMN avatar_thumb_file LONGBLOB'
-
To avoid performance problems loading all of the BLOB columns every time you access your ActiveRecord object, a class method is provided on your model called “select_without_file_columns_for.” This is set to a hash that will instruct ActiveRecord::Base.find to load all of the columns except the BLOB/file data columns, for example:
{:select=>"id,name,avatar_file_name,avatar_content_type,..."}
If you’re using Rails 2.3, you can specify this as a default scope:
default_scope select_without_file_columns_for(:avatar)
Or if you’re using Rails 2.1 or 2.2 you can use it to create a named scope:
named_scope :without_file_data, select_without_file_columns_for(:avatar)
- By default, URLs will be set to this pattern:
/:relative_root/:class/:attachment/:id?style=:style
Example:
/app-root-url/users/avatars/23?style=original
The idea here is that to retrieve a file from the database storage, you will need some controller's code to be executed. Once you pick a controller to use for downloading, you can add this line to generate the download action for the default URL/action (the plural attachment name), "avatars" in this example:
downloads_files_for :user, :avatar
Or you can write a download method manually if there are security, logging or other requirements. If you prefer a different URL for downloading files you can specify that in the model; e.g.:
has_attached_file :avatar,
:storage => :database,
:url =>'/users/show_avatar/:id/:style'
- Add a route for the download to the controller which will handle downloads, if necessary. The default URL, /:relative_root/:class/:attachment/:id?style=:style, will be matched by the default route: :controller/:action/:id
For now, I’ve overwritten my code from February. I believe this implementation is cleaner and easier to use; if anyone did download and use my code from February let me know and I can help you migrate the file data from the separate table back to columns in primary table. I will be writing a script to do this for my own application.
When I have time in the next few days or weeks, I’ll post a sample application that illustrates all of these steps and shows exactly how to do all of this. If anyone has any questions or suggestions please let me know.
Tags:paperclip·rails
Update April 2009:
I just rewrote this to use the same database table as the target model to save the file data, and not a separate database table. The “create_table” migration I describe here below will no longer work; instead you should create one or more columns in the same table as the target model to hold the file data. Please read this update for more details.
Paperclip from Thoughtbot is a fantastic bit of code that allows you to easily upload files to your Rails app and later manage them as just another attribute on your model object. If you’re not familiar with Paperclip you should start by reading Thoughtbot’s Paperclip intro page; Ryan Bates also did a screen cast on Paperclip usage. By default it supports saving the file attachments on the server file system, and also there’s an option for saving the files in Amazon’s S3 service. One reason I decided to use Paperclip in a recent project was that the implementation and usage were both much simpler and cleaner than attachment_fu, the other popular Rails plugin for file upload and management.
One thing that Thoughtbot decided not to implement was the ability to store files in a database table, rather than on the file system. It doesn’t make a lot of sense to do this for most normal web application deployments, since serving files via Apache directly from the file system is obviously much faster and avoids the need to call your Rails stack at all for download requests. However, I work in an Enterprise IT environment that has a lot of experience with Oracle, and finds it easier to manage file attachments with a database table. I also have requirements around file encryption, security, etc.
Since Paperclip doesn’t include a database storage option, I decided to write one. Here’s what I came up with: http://github.com/patshaughnessy/paperclip
I added a new storage module called Paperclip::Storage::Database. See lib/paperclip/storage.rb for details; Paperclip::Storage::Database is at the bottom of the file. I’d love any feedback or suggestions about the usage/design of how the database storage option would work with your application, or on the implementation itself.
I’ll be blogging here in the coming weeks with a detailed explanation of how Paperclip database storage works, and a working sample application that illustrates how to use it.
For now, here’s the usage description from lib/paperclip/storage.rb for specifying database storage for your Paperclip app. You need to follow these steps in addition to the standard Paperclip setup steps from Thoughtbot.
- In your model specify the "database" storage option; for example:
has_attached_file :avatar, :storage => :database
The files will be stored in a new database table named with the plural attachment name
by default, "avatars" in this example.
You need to create this new storage table with at least these columns:
- file_contents
- style
- the primary key for the parent model (e.g. user_id)
Note the "binary" migration will not work for the LONGBLOG type in MySQL for the
file_contents column. You may need to craft a SQL statement for your migration,
depending on which database server you are using. Here's an example migration for MySQL:
create_table :avatars do |t|
t.string :style
t.integer :user_id
t.timestamps
end
execute 'ALTER TABLE avatars ADD COLUMN file_contents LONGBLOB'
You can optionally specify any storage table name you want as follows:
has_attached_file :avatar, :storage => :database,
:database_table => 'avatar_files'
- By default, URLs will be set to this pattern:
/:relative_root/:class/:attachment/:id?style=:style
Example:
/app-root-url/users/avatars/23?style=original
The idea here is that to retrieve a file from the database storage, you will need some
controller's code to be executed. Once you pick a controller to use for downloading, you can add this line
to generate the download action for the default URL/action (the plural attachment name),
"avatars" in this example:
downloads_files_for :user, :avatar
Or you can write a download method manually if there are security, logging or other
requirements. If you prefer a different URL for downloading files you can specify that in the model; e.g.:
has_attached_file :avatar, :storage => :database,
:url => '/users/show_avatar/:id/:style'
- Add a route for the download to the controller which will handle downloads, if necessary.
The default URL (/:relative_root/:class/:attachment/:id?style=:style) will be matched by
the default route:
map.connect ':controller/:action/:id'
Tags:paperclip·paperclip·rails·rails