Serving Images Stored in the Database with Rails
10:46 AM EDT Saturday, April 19 2008
Some Rails applications accept images uploaded by users via the application, such as profile images. You have a few choices as to how to store the image. I'm not going to go over them or debate the merits of each in this article. Instead I'm going to cover how to handle it if you've decided that you want to store your images in the database.
So to get started with the example, create a rails app and an image model to hold the image data:
$ rails myapp
$ cd myapp
$ script/generate model image file_name:string content_type:string \
file_size:integer file_data:binary
$ rake db:migrate
Ok, now let's put the awesomest image ever into our database:
$ script/console
>> require 'net/http'
=> []
>> host = 'farm1.static.flickr.com'
=> "farm1.static.flickr.com"
>> path = '/62/163977533_55fd5ddd9b_o_d.jpg'
=> "/62/163977533_55fd5ddd9b_o_d.jpg"
>> res = Net::HTTP.get_response(host, path)
=> #<Net::HTTPOK 200 OK readbody=true>
>> Image.create!(:file_name => "awesome.jpg",
:content_type => res['Content-type'],
:file_size => res.body.size,
:file_data => res.body)
=> #<Image id: 1, file_name: "awesome.jpg"...
I've added some spacing for dramatic effect. Now, create an images controller at app/controllers/images_controller.rb:
class ImagesController < ApplicationController
caches_page :show
def show
if @image = Image.find_by_file_name(params[:file_name])
send_data(
@image.file_data,
:type => @image.content_type,
:filename => @image.file_name,
:disposition => 'inline'
)
else
render :file => "#{RAILS_ROOT}/public/404.html", :status => 404
end
end
end
Then setup a route:
map.connect "/images/*file_name", :controller => "images", :action => "show"
Next make sure caching is turned on for your development environment. Edit this line in config/environments/development.rb:
config.action_controller.perform_caching = true
And finally, start up rails and view the image in your browser at http://localhost:3000/images/awesome.jpg. If you look in your log, you will see a line like this:
Cached page: /images/awesome.jpg (0.00100)
But if you refresh your browser or hit the same URL from a different browser, you will not see that anymore. That's because we've cached the file to local disk and it can be served directly from there by the web server without going through rails. Mongrel does this by default on your local development setup, but your production environment might require some configuration to make sure that happens. The guys at Rails envy have a great article that has the details on how to configure our web and application servers to do that. The article provides more detail on page caching as well, including how to clear the cache when images change, so you should definitely check that out.




