Recipe 15.2. Uploading Images to a Database


Problem

You want your application to accept uploaded images and store them in a database.

Solution

Create items and photos tables, setting them up with a one-to-many relationship with each other:

db/migrate/001_build_db.rb:

class BuildDb < ActiveRecord::Migration   def self.up     create_table :items do |t|       t.column :name,         :string       t.column :description,  :text     end     create_table :photos do |t|       t.column :item_id,      :integer       t.column :name,         :string       t.column :content_type, :string       t.column :data,         :binary     end   end   def self.down     drop_table :photos     drop_table :items   end end

Modify your form in new.rhtml to handle file uploads by adding the :multipart=>true option to the form_tag helper, and a call to the file_field helper to add a file selection box:

app/views/items/new.rhtml:

<h1>New item</h1> <% form_tag( {:action=>'create'}, :multipart=>true ) do %>   <% if flash[:error] %>     <div ><%= flash[:error] %></div>   <% end -%>   <p><label for="item_name">Name</label><br />   <%= text_field 'item', 'name'  %></p>   <p><label for="item_description">Description</label><br />   <%= text_area 'item', 'description', :rows => 5  %></p>   <p><label for="photo">Photo</label><br />   <%= file_field("photo", "photo", :class => 'textinput') %>   <%= submit_tag "Create" %> <% end %>

The Items Controller needs the following added to its create method:

app/controllers/items_controller.rb:

class ItemsController < ApplicationController   def list     @item_pages, @items = paginate :items, :per_page => 10   end   def show     @item = Item.find(params[:id])   end   def new   end   def create     @item = Item.new(params[:item])     if @item.save       flash[:error] = 'There was a problem.'       redirect_to :action => 'new'       return       end     unless params[:photo]['photo'].content_type =~ /^image/       flash[:error] = 'Please select an image file to upload.'       render :action => 'new'       return       end     @photo = Photo.new(params[:photo])     @photo.item_id = @item.id     if @photo.save       flash[:notice] = 'Item was successfully created.'       redirect_to :action => 'list'     else           flash[:error] = 'There was a problem.'       render :action => 'new'     end   end end

Your item models should then specify that items have many photos:

app/models/item.rb:

class Item < ActiveRecord::Base   has_many :photos  end

The photo model should include a belongs_to statement, associating photos with items. Here is also where you define the photo method that is used in the Items Controller.

app/models/photo.rb:

class Photo < ActiveRecord::Base   belongs_to :item   def photo=(image_field)     self.name = base_part_of(image_field.original_filename)     self.content_type = image_field.content_type.chomp     self.data = image_field.read   end   def base_part_of(file_name)     name = File.basename(file_name)     name.gsub(/[^\w._-]/, '')   end end

Discussion

One decision to be made when uploading files to an application is whether to store the files entirely in a database or on the filesystem with only path information in the database. You should decide which approach is best for your situation based on the pros and cons of each. This solution does the former and stores uploaded image files in MySQL as blob datatypes.

The solution adds a file-upload field to the item-creation form. The empty new method of the Items controller instructs Rails to process the items/new.rhtml template. The template in turn, sends parameters for both Item and Photo objects back to the controllers create method for processing.

The create method instantiates a new Item object and attempt to save it. The Item object is saved first so that you have its ID to pass to the Photo object. Next, the solution performs some error checking on the uploaded file's content type. If it's not an image, repaint the form with a message saying so.

The first two parameters of the file_field helper are both photo, producing the following name for file-selection HTML element: name="photo[photo]", or "object[method]". When the form is submitted, this name indicates that the file component of the form will be used to instantiate a new Photo object in the controller, and the photo method of the model will be invoked to load that object with the actual file data. The file's name, content type, and body are stored in the corresponding attributes of the object.

Back in the controller, assign the item ID (@item.id) from the newly created Item object to the item_id attribute of the Photo object. Finally, the Photo object is saved, and if you're successful, redirected to a listing of all your items.

The file_field helper adds the file selection widget to the form. Figure 15-1 shows the solution's Item creation form including the option for file selection.

Figure 15-1. A form with a file selection field for uploading images


After a successful upload, an item listing is displayed with the option to view the details of each item.

See Also

  • Section 14.8"




Rails Cookbook
Rails Cookbook (Cookbooks (OReilly))
ISBN: 0596527314
EAN: 2147483647
Year: 2007
Pages: 250
Authors: Rob Orsini

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net