Skip to content
Squeegy edited this page Sep 12, 2010 · 10 revisions

Your users need a way to upload their images into your site. Here is how we might render a form to create a Photo record.

# app/views/photos/new.html.erb

<% form_for @photo, :html => { :multipart => true } do |f| %>
  <p>
    <b>Name</b><br />
    <%= f.text_field :name %>
  </p>

  <p>
    <b>Author</b><br />
    <%= f.text_field :author %>
  </p>

  <p>
    <b>Upload Image</b><br />
    <%= f.file_field :image_file %><br />
    or URL: <%= f.text_field :image_file_url %>
  </p>

  <p>
    <%= f.submit "Create" %>
  </p>
<% end %>

IMPORTANT:

The :html => { :multipart => true } is VERY IMPORTANT. Without this snippet your browser will not send binary data to the server. If things aren’t working, check to make sure you have this in your form declaration.

The relevant bit of the form code for uploading is:

<p>
  <b>Upload Image</b><br />
  <%= f.file_field :image_file %><br />
  or URL: <%= f.text_field :image_file_url %>
</p>

First there is a file upload field, mapping the the image_file attribute of our model. When the user browses to a local file and uploads it, the model is waiting to accept an uploaded file on this attribute and will automatically open it and save it to disk for you.

Right along side the upload field is a simple text field, which maps to the image_file_url property. Your model also listens for assignment to this attribute to automatically fetch the contents of a URL and save it locally as the master image.

You can have just one of these fields, or both. The model will know how to do the right thing either way.

When the user submits the form, all you have to do is assign the form contents to your object in standard Rails fashion, and the image is uploaded and saved for you. Creating a new photo may look like this in your controller, which is exactly how you would create almost any model from a Rails form:

# app/controllers/photos_controller.rb
def create
  @photo = Photo.new(params[:photo])
  if @photo.save
    redirect_to photo_url(@photo)
  else
    flash[:notice] = 'Your photo did not pass validation!'
    render :action => 'new'
  end
end

Redisplay Safe Upload Form

Now let’s say you have a model with validations. Perhaps a Photo which requires a title. Someone uploads a file from their computer, but doesn’t put in a name. Most Rails app would redisplay the form with validation errors. At this point the uploaded file is lost and must be found again, and uploaded again. Thanks to some ideas from hmans, Fleximage supports form redisplay gracefully.

Simply add to your form template to look like this:

<p>
  <b>Upload Image</b><br />
  <%= f.file_field :image_file %><br />
  or URL: <%= f.text_field :image_file_url %>
  <%= f.hidden_field :image_file_temp %>
</p>

<p>
  <b>Uploaded Image:</b><br />
  <%= embedded_image_tag(@photo.operate { |img| img.resize 100 }) if @photo.has_image? %>
</p>

We have added 2 things here. First is the hidden_field :image_file_temp. Before Fleximage permanently saves an uploaded file, it will stick a copy of the upload in your Rails tmp directory. Your model is then set to remember the name of this temporary file. When your form is redisplayed, this hidden field holds the name of this temp file. When posted back to the application, Fleximage loads the file from the temp directory so that the user doesn’t have to click browse and re-upload their image just because they forgot the title or some other unrelated validation error.

The embedded_image_tag line offers a preview image of what was just uploaded, even if its not saved yet. It works just like a regular image_tag, except you pass in a model object rather than a string file path. You can even run an inline operate on the output image to keep the size within sane margins.

Now you can upload a file once, and it will persist for as many form redisplays as it takes to get your record saved.

Next: Linking

Clone this wiki locally