Nov 25

Date published: 11-25-2013

Rails version: 4.0.0

File upload

Web sites are a very visual form of communication, and so it’s not that uncommon for users to want to add photographs to their sites. Most sites let you load an avatar for your user page, and some sites like flickr and Photobucket are based around hosting images. In this tutorial, we’re going to use a gem called Carrierwave upload photos and display them on a web page.

Photo upload

Photo upload

Copy the template site

The first thing we need to do is copy our template site and create a new project from it. Open a terminal window and switch to the directory where the template Bootstrap site is located. Run the following commands:

$ cp -r template_bootstrap_site/ photo_upload
$ cd photo_upload/

It’s a good idea now to change the module name just like we did in this tutorial in the section titled, “Update the module information.” Make sure to commit your changes to Git before moving on.

Add the Carrierwave gem

Open the Gemfile and add the following line:

gem 'carrierwave', '0.9.0'
gem 'mini_magick', '3.6.0'

We need to run a few commands in the console to create the files we need. We need to install Imagemagick to support resizing of images, and I’m using a Mac with Homebrew. If you’re on a different setup, you’ll need to manually install Imagemagick and ignore the first command. Switch to the console and run these commands:

$ brew install imagemagick
$ bundle install
$ rails generate uploader Photo
      create  app/uploaders/photo_uploader.rb

If you open the uploader that was created, you should see a few commands along with a lot of comments. Notice that a method has been defined called store_dir. We can change this to specify where we want uploaded photos to be saved on the server. We’ll leave it as it is, but we need to make one change to make sure uploaded photos don’t get committed to Git. Open the .gitignore file and add the following line at the bottom:

uploads/

This will make sure that any files that are uploaded won’t get saved when we commit to Git. We need to make some changes to the photo_uploader.rb file to accommodate what we want to do with our photos, but first we need to define what our requirements are. We only want users to be able to upload photo files, which we can do by uncommenting the extension_white_list method that’s already provided. We’re going to show a gallery page of all photos that have been uploaded and a page that shows individual photos when we click on one in the gallery. Add the following to app/uploaders/photo_uploader.rb:

  process :resize_to_fit => [800, 800]

  version :thumb do
    process :resize_to_fill => [200,200]
  end

  def extension_white_list
    %w(jpg jpeg gif png)
  end

The call to process will be called on the photo that is uploaded ensuring that the photo we save is resized so that the longest side is a maximum of 800 pixels. This will keep it from saving huge files because we know we only want to display that maximum size of photo on our pages. The call to version tells Carrierwave that we want a version of the photo called thumb that is resized to be 200 x 200 pixels. We’ll use that thumb on our gallery page.

Create the photo model and views

The next thing we need to do is create a model to hold the photos. Switch to the console and run the following commands:

$ rails g model photo photo_file:string
      invoke  active_record
      create    db/migrate/20131125182817_create_photos.rb
      create    app/models/photo.rb
$ rake db:migrate
==  CreatePhotos: migrating ===================================================
-- create_table(:photos)
   -> 0.0024s
==  CreatePhotos: migrated (0.0030s) ==========================================
$ annotate
Annotated (1): Photo

The new file is basically an empty model. We need to tell Rails we want the photo file to be uploaded using the photo_uploader we just made. Add this line to app/models/photo.rb:

mount_uploader :photo_file, PhotoUploader

That’s all we have to do in our model, so let’s shift our attention to the views. The first thing we should do is add a call in config/routes.rb for our photo controller:

resources :photos, only: [:new, :create, :show]

Next we need to create our controller and define the actions to add new and show our photos. Create a new file call app/controllers/photos_controller.rb:

class PhotosController < ApplicationController
  def new
    @photo = Photo.new
  end

  def create
    @photo = Photo.new(photo_params)

    if @photo.save
      redirect_to root_path
    else
      render 'new'
    end
  end

  def show
    @photo = Photo.find(params[:id])
  end

  private
  def photo_params
    params.require(:photo).permit(:photo_file)
  end
end

While we’re modifying controllers, let’s make a change to app/controllers/static_pages_controller.rb to get all photos:

class StaticPagesController < ApplicationController
  def home
    @photos = Photo.all
  end

  def about
  end
end

Now we need to create new views for pages to upload photos and view the photos that have been uploaded. Let’s update our header so we have a convenient link to the page to upload a photo. Open app/views/layouts/_header.html.erb and make these changes:

        <%= link_to "Photos", root_path, class: "brand" %>
        <div class="nav-collapse collapse">
          <ul class="nav">
            <li><%= link_to "New photo", new_photo_path %></li>
          </ul>

Let’s also update our home page to show a gallery of all the photos that have been uploaded. Change app/views/static_pages/home.html.erb to look like this:

<% provide(:title, "Home Page") %>
<div class="row">
  <p>There are <%= @photos.count %> photos.</p>
  <% @photos.each do |photo| %>
    <%= link_to image_tag(photo.photo_file.thumb.url), photo %>
  <% end %>
</div>

Next let’s make the page to upload new photos. Create a file called app/views/photos/new.html.erb and fill it in with this:

<% provide(:title, "New Photo") %>
<h1>New Photo</h1>
<%= form_for @photo, :html => {:multipart => true} do |f| %>
  <p>
    <label>Photo</label>
    <%= f.file_field :photo_file %>
    <%= f.hidden_field :photo_file_cache %>
  </p>

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

We still need a page to show the images when the user clicks on a photo in the gallery. Create a new file at app/views/photos/show.html.erb and fill it in with this:

<% provide(:title, "Show Photo") %>
<center>
  <%= image_tag @photo.photo_file.url %>
</center>

That’s all we need to do to get it working. Restart the Rails server and try out adding new photos. They should show up in the gallery on our home page, and if you click an individual photo, it should redirect you to the page to view the bigger photo. Since we’re done, let’s commit our changes in Git so we don’t lose them:

$ git add .
$ git commit -m "Added photo uploading"

The source code for this tutorial can be found on Github at:

http://github.com/wordplay/photo_upload

Leave a Reply

preload preload preload