Tuesday, March 11, 2014

Carrierwave File Uploads

Step 1: Installing CarrierWave

The first step is to add the CarrierWave gem to our application. The gem is installed in the usual way by adding a reference to it in the application’s Gemfile.

In /Gemfile,

gem 'carrierwave'

We’ll need to run bundle to make sure that the gem is installed on our system. Once it has installed the first thing we’ll need to do is generate an uploader class. CarrierWave provides a generator called uploader to do this to which we pass the name we want to give our uploader, in this case image.

$ rails g uploader image
      create  app/uploaders/image_uploader.rb

The generator creates a new directory called uploaders under the app directory and in it a file called image_uploader.rb. In this file are a number of comments explaining how to customize the uploader. For example there is code to change the upload location, perform processing on the image after uploading and to restrict the type of files that can be uploaded. We’ll leave the defaults as they are for now and come back and make some customizations later.

Next we’ll need to add the uploader to the Post model. We’ll need a column in the posts table to store it in so we’ll generate a migration to do add it.

$ rails g migration add_image_to_posts image:string

Note that the new column is a string and has the same name as we gave the uploader when we generated it. We’ll need to run $ rake db:migrate to update the database.

Next we’ll add the uploader to the Post model which is done by calling the mount_uploader method, passing in the column name that we generated above and the class for the uploader. We also need to add the image column to the list in the attr_accessible call so that it can be accessed from the controllers.

In /app/models/post.rb,

class Post < ActiveRecord::Base
  attr_accessible :body, :title, :author_id, :image

  belongs_to :author

  has_many :comments

  has_many :favourites

  has_many :post_tags
  has_many :tags, through: :post_tags

  has_and_belongs_to_many :labels

  mount_uploader :image, ImageUploader

  validates_presence_of :body, :title
end

With this in place we can modify the form for adding or modifying a post so that it has a file upload field.

In /app/views/posts/_form.html.erb,

<%= form_for(@post) do |f| %>
  <% if @post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% @post.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="field">
    <%= f.label :author %><br />
    <%= collection_select(:post, :author_id, Author.all, :id, :name) %>
  </div>
  <div class="field">
    <%= f.label :image %><br />
    <%= f.file_field :image %> 
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

Step 2: Securing Uploads

Certain file might be dangerous if uploaded to the wrong location, such as php files or other script files. CarrierWave allows you to specify a white-list of allowed extensions.

If you're mounting the uploader, uploading a file with the wrong extension will make the record invalid instead. Otherwise, an error is raised.

class MyUploader < CarrierWave::Uploader::Base
  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

Step 3: Viewing The Uploaded Images

In /app/views/posts/show.html.erb,

  <h2>Images</h2>
  <div id="images">
    <%= image_tag @post.image_url %>
  </div>

Step 4: Uploading Images via a URL

Another feature that CarrierWave provides is the ability to add photographs via a URL instead of uploading them directly. We can do this by adding another field to the upload form called remote_image_url. The name is important, when CarrierWave sees a field with that name it knows that it has to fetch the image from a URL.

In /app/views/posts/_form.html.erb,

<%= form_for(@post) do |f| %>
  <% if @post.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>

      <ul>
      <% @post.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= f.label :title %><br />
    <%= f.text_field :title %>
  </div>
  <div class="field">
    <%= f.label :body %><br />
    <%= f.text_area :body %>
  </div>
  <div class="field">
    <%= f.label :author %><br />
    <%= collection_select(:post, :author_id, Author.all, :id, :name) %>
  </div>
  <div class="field">
    <%= f.label :image %><br />
    <%= f.file_field :image %><br />
    <%= f.label :remote_image_url, "or image URL" %> 
    <%= f.text_field :remote_image_url %> 
  </div>
  <div class="actions">
    <%= f.submit %>
  </div>
<% end %>

As we’ve added a field to the post form we’ll need to add it to the list of attr_accessible fields in the Post model.

In /app/models/post.rb,

class Post < ActiveRecord::Base
  attr_accessible :body, :title, :author_id, :image, :remote_image_url

  belongs_to :author

  has_many :comments

  has_many :favourites

  has_many :post_tags
  has_many :tags, through: :post_tags

  has_and_belongs_to_many :labels

  mount_uploader :image, ImageUploader

  validates_presence_of :body, :title
end

Now in the form we’ll have a remote_image_url field and we can enter a URL in that field to add an image to the post without uploading it manually.

carrierwaveuploader/carrierwave · GitHub
Return to Internship Note (LoanStreet)
Previous Episode: Authentication with Devise
Next Episode: User Authorization with Rolify and Cancan

0 comments:

Post a Comment