<?xml version="1.0" encoding="UTF-8"?>
<feed xml:lang="en-US" xmlns="http://www.w3.org/2005/Atom">
  <title>PaulBarry.com - Serving Images Stored in the Database with Rails</title>
  <subtitle type="html">My thoughts, ideas, questions and concerns on technology, sports, music and life</subtitle>
  <id>tag:paulbarry.com,2007:Paulbarry.com</id>
  <generator uri="http://www.paulbarry.com" version="3.0">PaulBarry.com</generator>
  <link href="http://paulbarry.com/xml/atom/article/4889/feed.xml" rel="self" type="application/atom+xml"/>
  <link href="http://paulbarry.com/articles/2008/04/19/serving-images-stored-in-the-database-with-rails" rel="alternate" type="text/html"/>

  <updated>2008-11-03T18:20:42-05:00</updated>
  <entry>
    <author>
      <name>Paul Barry</name>
      <email>mail@paulbarry.com</email>
    </author>
    <id>urn:uuid:ee9e815d-5569-4d15-bbaf-7d6f8d06dca3</id>

    <published>2008-04-19T10:46:27-04:00</published>
    <updated>2008-04-19T10:46:27-04:00</updated>
    <title type="html">Serving Images Stored in the Database with Rails</title>
    <link href="http://paulbarry.com/articles/2008/04/19/serving-images-stored-in-the-database-with-rails" rel="alternate" type="text/html"/>

    <category term="technology" scheme="http://paulbarry.com/articles/category/technology" label="Technology"/>
        <category term="Rails" scheme="http://paulbarry.com/articles/tag/rails"/>
    <category term="Ruby" scheme="http://paulbarry.com/articles/tag/ruby"/>
    <category term="Caching" scheme="http://paulbarry.com/articles/tag/caching"/>
        <summary type="html">&lt;p&gt;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&apos;m not going to go over them or debate the merits of each in this article.  Instead I&apos;m going to cover how to handle it if you&apos;ve decided that you want to store your images in the database.  &lt;/p&gt;

&lt;p&gt;So to get started with the example, create a rails app and an image model to hold the image data:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rails myapp
$ cd myapp
$ script/generate model image file_name:string content_type:string \
file_size:integer file_data:binary
$ rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ok, now let&apos;s put the awesomest image ever into our database:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ script/console 

&amp;gt;&amp;gt; require &apos;net/http&apos;
=&amp;gt; []

&amp;gt;&amp;gt; host = &apos;farm1.static.flickr.com&apos;
=&amp;gt; &quot;farm1.static.flickr.com&quot;

&amp;gt;&amp;gt; path = &apos;/62/163977533_55fd5ddd9b_o_d.jpg&apos;
=&amp;gt; &quot;/62/163977533_55fd5ddd9b_o_d.jpg&quot;

&amp;gt;&amp;gt; res = Net::HTTP.get_response(host, path)
=&amp;gt; #&amp;lt;Net::HTTPOK 200 OK readbody=true&amp;gt;

&amp;gt;&amp;gt; Image.create!(:file_name =&amp;gt; &quot;awesome.jpg&quot;, 
  :content_type =&amp;gt; res[&apos;Content-type&apos;], 
  :file_size =&amp;gt; res.body.size, 
  :file_data =&amp;gt; res.body)
=&amp;gt; #&amp;lt;Image id: 1, file_name: &quot;awesome.jpg&quot;...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I&apos;ve added some spacing for dramatic effect.  Now, create an images controller at &lt;code&gt;app/controllers/images_controller.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class ImagesController &amp;lt; ApplicationController
  caches_page :show
  def show
    if @image = Image.find_by_file_name(params[:file_name])
      send_data(
        @image.file_data, 
        :type =&amp;gt; @image.content_type,
        :filename =&amp;gt; @image.file_name,
        :disposition =&amp;gt; &apos;inline&apos;
      )
    else
      render :file =&amp;gt; &quot;#{RAILS_ROOT}/public/404.html&quot;, :status =&amp;gt; 404
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then setup a route:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;map.connect &quot;/images/*file_name&quot;, :controller =&amp;gt; &quot;images&quot;, :action =&amp;gt; &quot;show&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next make sure caching is turned on for your development environment.  Edit this line in &lt;code&gt;config/environments/development.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;config.action_controller.perform_caching = true
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Cached page: /images/awesome.jpg (0.00100)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But if you refresh your browser or hit the same URL from a different browser, you will not see that anymore.  That&apos;s because we&apos;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 &lt;a href=&quot;http://www.railsenvy.com/2007/2/28/rails-caching-tutorial&quot;&gt;a great article that has the details on how to configure our web and application servers to do that&lt;/a&gt;.  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.&lt;/p&gt;
</summary>
    <content type="html">&lt;p&gt;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&apos;m not going to go over them or debate the merits of each in this article.  Instead I&apos;m going to cover how to handle it if you&apos;ve decided that you want to store your images in the database.  &lt;/p&gt;

&lt;p&gt;So to get started with the example, create a rails app and an image model to hold the image data:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ rails myapp
$ cd myapp
$ script/generate model image file_name:string content_type:string \
file_size:integer file_data:binary
$ rake db:migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Ok, now let&apos;s put the awesomest image ever into our database:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;$ script/console 

&amp;gt;&amp;gt; require &apos;net/http&apos;
=&amp;gt; []

&amp;gt;&amp;gt; host = &apos;farm1.static.flickr.com&apos;
=&amp;gt; &quot;farm1.static.flickr.com&quot;

&amp;gt;&amp;gt; path = &apos;/62/163977533_55fd5ddd9b_o_d.jpg&apos;
=&amp;gt; &quot;/62/163977533_55fd5ddd9b_o_d.jpg&quot;

&amp;gt;&amp;gt; res = Net::HTTP.get_response(host, path)
=&amp;gt; #&amp;lt;Net::HTTPOK 200 OK readbody=true&amp;gt;

&amp;gt;&amp;gt; Image.create!(:file_name =&amp;gt; &quot;awesome.jpg&quot;, 
  :content_type =&amp;gt; res[&apos;Content-type&apos;], 
  :file_size =&amp;gt; res.body.size, 
  :file_data =&amp;gt; res.body)
=&amp;gt; #&amp;lt;Image id: 1, file_name: &quot;awesome.jpg&quot;...
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;I&apos;ve added some spacing for dramatic effect.  Now, create an images controller at &lt;code&gt;app/controllers/images_controller.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;class ImagesController &amp;lt; ApplicationController
  caches_page :show
  def show
    if @image = Image.find_by_file_name(params[:file_name])
      send_data(
        @image.file_data, 
        :type =&amp;gt; @image.content_type,
        :filename =&amp;gt; @image.file_name,
        :disposition =&amp;gt; &apos;inline&apos;
      )
    else
      render :file =&amp;gt; &quot;#{RAILS_ROOT}/public/404.html&quot;, :status =&amp;gt; 404
    end
  end
end
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Then setup a route:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;map.connect &quot;/images/*file_name&quot;, :controller =&amp;gt; &quot;images&quot;, :action =&amp;gt; &quot;show&quot;
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;Next make sure caching is turned on for your development environment.  Edit this line in &lt;code&gt;config/environments/development.rb&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;config.action_controller.perform_caching = true
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;Cached page: /images/awesome.jpg (0.00100)
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;But if you refresh your browser or hit the same URL from a different browser, you will not see that anymore.  That&apos;s because we&apos;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 &lt;a href=&quot;http://www.railsenvy.com/2007/2/28/rails-caching-tutorial&quot;&gt;a great article that has the details on how to configure our web and application servers to do that&lt;/a&gt;.  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.&lt;/p&gt;
</content>
  </entry>
  <entry>
    <author>
      <name>James Edward Gray II</name>
    </author>
    <id>urn:uuid:2f60a942-2142-4668-b3c5-33e35606ddc5</id>
    <published>2008-05-15T09:17:04-04:00</published>
    <updated>2008-05-15T09:17:04-04:00</updated>
    <title type="html">Comment on "Serving Images Stored in the Database with Rails" by James Edward Gray II</title>
    <link href="http://paulbarry.com/articles/2008/04/19/serving-images-stored-in-the-database-with-rails#comment-5138" rel="alternate" type="text/html"/>
    <content type="html">I do feel it&apos;s worth pointing out the major downside of this approach:  the entire image is loaded into a Ruby String anytime you are working with that model (by default).  This can get to be a significant burden on a large traffic site with serving many images.  I had a database teacher who always use to remind us, &amp;quot;File data belongs in a file system.&amp;quot;</content>
  </entry>
  <entry>
    <author>
      <name>Paul Barry</name>
    </author>
    <id>urn:uuid:c03b839f-9f53-45c6-ac07-e5ea73b5197f</id>
    <published>2008-05-15T10:25:52-04:00</published>
    <updated>2008-05-15T10:25:52-04:00</updated>
    <title type="html">Comment on "Serving Images Stored in the Database with Rails" by Paul Barry</title>
    <link href="http://paulbarry.com/articles/2008/04/19/serving-images-stored-in-the-database-with-rails#comment-5139" rel="alternate" type="text/html"/>
    <content type="html">@James&lt;br/&gt;&lt;br/&gt;That is true, this example was meant to mostly show how to download the image and use caching.  For a real app, I would use attachment_fu and store the file data in a separate table, and attachment_fu supports that with the db_file option.  The only time you even load that record would be in this controller, when the file gets cached.&lt;br/&gt;&lt;br/&gt;One advantage of keeping the data in the DB is that there are no extra operations required to keep the file on the file system and record in the DB in sync.  Also, you can just backup your database and you get the file data right along with it.  Also if you have more than one app server, the file data is stored in a central location.  But on a large app, storing a lot of file data in the DB could make the DB harder to manage, so storing the file data on a shared file system would make more sense.  But for small to medium sites, as long as you keep the file data in a separate table from the record that has the data about the file, I prefer to keep everything the DB, YMMV.</content>
  </entry>
  <entry>
    <author>
      <name>Ariejan de Vroom</name>
    </author>
    <id>urn:uuid:b83ccabb-45c1-4f7a-ba59-de31aff02477</id>
    <published>2008-09-11T08:38:42-04:00</published>
    <updated>2008-09-11T08:38:42-04:00</updated>
    <title type="html">Comment on "Serving Images Stored in the Database with Rails" by Ariejan de Vroom</title>
    <link href="http://paulbarry.com/articles/2008/04/19/serving-images-stored-in-the-database-with-rails#comment-5194" rel="alternate" type="text/html"/>
    <content type="html">Storing files in a database is not really a problem, since most database servers have great support for it.&lt;br/&gt;&lt;br/&gt;I use this approach to store small thumbnails in my database. By using a has_many relation I can easily access my data without loading all the Photo data. I only load it when I need it. In combination with the caching feature, this is a very acceptable solution. </content>
  </entry>
  </feed>