Advertisement
  1. Code
  2. Ruby on Rails

Rails Image Upload: Using Paperclip in a Rails Application

Scroll to top
Read Time: 6 min

In the first two parts of this series, I showed you how to enable image uploading in Rails using CarrierWave. In this part, you will see how to do so using Paperclip.

Paperclip is a Ruby gem provided by thoughtbot. It was created to make file attachment very easy. In this tutorial, you will see how to use Paperclip alongside Devise.

Without much talk, let's get busy.

Paperclip requires the installation of ImageMagick on your machine. You need this for image processing. To install ImageMagick, use any of the steps below, depending on the type of machine you use.

Mac Users:

brew install imagemagick

Ubuntu users:

sudo apt-get install imagemagick

Rails Application Generation

Use your terminal to generate a new application.

rails new paperclip

Open up your Gemfile and add the necessary gems:

gem 'paperclip'

gem 'devise'

Run bundle install when you are done.

Devise Setup

From your terminal, install devise using the command below:

rails generate devise:install

When that is done, you can now generate your User model:

rails generate devise User

Migrate your database after.

rake db:migrate

Generate your devise views.

rails generate devise:views

Using your text editor, navigate to app/views/layouts/application.html.erb and add the following code just above the yield block.

1
#app/views/layouts/application.html.erb
2
3
<p class="notice"><%= notice %></p>
4
<p class="alert"><%= alert %></p>

Paperclip Integration

Due to security reasons, we have to permit parameters in the Devise controller. Thanks to the awesome team behind Devise, doing this is easy.

Open up app/controllers/application_controller.rb and paste in the following lines of code.

1
#app/controllers/application_controller.rb
2
3
class ApplicationController < ActionController::Base
4
  # Prevent CSRF attacks by raising an exception.
5
  # For APIs, you may want to use :null_session instead.
6
7
  protect_from_forgery with: :exception
8
  
9
  before_action :configure_permitted_parameters, if: :devise_controller?
10
11
  protected
12
13
  def configure_permitted_parameters
14
    devise_parameter_sanitizer.for(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation, :remember_me, :avatar, :avatar_cache) }
15
    devise_parameter_sanitizer.for(:account_update) { |u| u.permit(:username, :password, :password_confirmation, :current_password, :avatar) }
16
  end
17
end
18

Open up your User model and make it look like this:

1
#app/models/user.rb
2
3
class User < ActiveRecord::Base
4
  # Include default devise modules. Others available are:
5
  # :confirmable, :lockable, :timeoutable and :omniauthable
6
  devise :database_authenticatable, :registerable,
7
         :recoverable, :rememberable, :trackable, :validatable
8
9
  has_attached_file :avatar, styles: { medium: "300x300", thumb: "100x100" }
10
  validates_attachment_content_type :avatar, content_type: /\Aimage\/.*\Z/
11
end

You need to add an avatar column to your Users table. There is a rails command that makes this possible from your terminal.

rails generate migration add_avatar_to_users

That will create a new migration in db/migrate. Open it up and paste the below code:

1
class AddAvatarToUsers < ActiveRecord::Migration
2
  def up
3
    add_attachment :users, :avatar
4
  end
5
6
  def down
7
    remove_attachment :users, :avatar
8
  end
9
end

Run your migration

rake db:migrate

Add Avatar to Devise Forms

You will edit your registration new form app/views/devise/registrations/new.html.erb and edit the form app/views/devise/registrations/edit.html.erb to what I have below:

1
#app/views/devise/registrations/new.html.erb
2
3
<h2>Sign up</h2>
4
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { multipart: true }) do |f| %>
5
  <%= devise_error_messages! %>
6
  <div class="field">
7
    <%= f.label :email %><br />
8
    <%= f.email_field :email, autofocus: true %>
9
  </div>
10
11
  <div class="field">
12
    <%= f.label :password %>
13
    <% if @minimum_password_length %>
14
    <em>(<%= @minimum_password_length %> characters minimum)</em>
15
    <% end %><br />
16
    <%= f.password_field :password, autocomplete: "off" %>
17
  </div>
18
19
  <div class="field">
20
    <%= f.label :password_confirmation %><br />
21
    <%= f.password_field :password_confirmation, autocomplete: "off" %>
22
  </div>
23
24
  <div class="field">
25
    <%= f.file_field :avatar %>
26
  </div>
27
28
  <div class="actions">
29
    <%= f.submit "Sign up" %>
30
  </div>
31
<% end %>
32
33
<%= render "devise/shared/links" %>
1
#app/views/devise/registrations/edit.html.erb
2
3
<h2>Edit <%= resource_name.to_s.humanize %></h2>
4
5
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
6
  <%= devise_error_messages! %>
7
8
  <div class="field">
9
    <%= f.label :email %><br />
10
    <%= f.email_field :email, autofocus: true %>
11
  </div>
12
13
  <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
14
    <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
15
  <% end %>
16
17
  <div class="field">
18
    <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
19
    <%= f.password_field :password, autocomplete: "off" %>
20
  </div>
21
22
  <div class="field">
23
    <%= f.label :password_confirmation %><br />
24
    <%= f.password_field :password_confirmation, autocomplete: "off" %>
25
  </div>
26
27
  <div class="field">
28
    <%= f.file_field :avatar %>
29
  </div>
30
31
  <div class="field">
32
    <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
33
    <%= f.password_field :current_password, autocomplete: "off" %>
34
  </div>
35
36
  <div class="actions">
37
    <%= f.submit "Update" %>
38
  </div>
39
<% end %>
40
41
<h3>Cancel my account</h3>
42
43
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
44
45
<%= link_to "Back", :back %>

Kick off your browser and check out what you have.

For a standard application, you might want to check if a user who wants to edit his or her profile already has an avatar uploaded. This is easy to implement in your registration edit file.

Open up the registration edit file and make it look like this:

1
#app/views/devise/registrations/edit.html.erb
2
3
<h2>Edit <%= resource_name.to_s.humanize %></h2>
4
5
<%= form_for(resource, as: resource_name, url: registration_path(resource_name), html: { method: :put }) do |f| %>
6
  <%= devise_error_messages! %>
7
8
  <div class="field">
9
    <%= f.label :email %><br />
10
    <%= f.email_field :email, autofocus: true %>
11
  </div>
12
13
  <% if devise_mapping.confirmable? && resource.pending_reconfirmation? %>
14
    <div>Currently waiting confirmation for: <%= resource.unconfirmed_email %></div>
15
  <% end %>
16
17
  <div class="field">
18
    <%= f.label :password %> <i>(leave blank if you don't want to change it)</i><br />
19
    <%= f.password_field :password, autocomplete: "off" %>
20
  </div>
21
22
  <div class="field">
23
    <%= f.label :password_confirmation %><br />
24
    <%= f.password_field :password_confirmation, autocomplete: "off" %>
25
  </div>
26
27
  <div class="field">
28
    <%= f.file_field :avatar %>
29
30
    <% if @user.avatar? %>
31
      <%= image_tag @user.avatar.url(:thumb) %>
32
    <% end %>
33
  </div>
34
35
  <div class="field">
36
    <%= f.label :current_password %> <i>(we need your current password to confirm your changes)</i><br />
37
    <%= f.password_field :current_password, autocomplete: "off" %>
38
  </div>
39
40
  <div class="actions">
41
    <%= f.submit "Update" %>
42
  </div>
43
<% end %>
44
45
<h3>Cancel my account</h3>
46
47
<p>Unhappy? <%= button_to "Cancel my account", registration_path(resource_name), data: { confirm: "Are you sure?" }, method: :delete %></p>
48
49
<%= link_to "Back", :back %>

Can you see what changed?

In the above code, there is a conditional statement to check if an avatar already exists for a user using the line <% if @user.avatar? %>. If this returns true, the next line gets run, else it does not.

Security Validations

Validation is always important when enabling uploading features in your web application. Paperclip comes with measures to secure your application.

You can use any of the validations below in your model.

1
class User < ActiveRecord::Base
2
  has_attached_file :avatar
3
  # Validate content type
4
  validates_attachment_content_type :avatar, content_type: /\Aimage/
5
  # Validate filename
6
  validates_attachment_file_name :avatar, matches: [/png\Z/, /jpe?g\Z/]
7
  # Explicitly do not validate
8
  do_not_validate_attachment_file_type :avatar
9
end

Conclusion

You might want to consider Paperclip as you build your next web application. It has a great team supporting it.

To explore other features not covered in this tutorial, check Paperclip's GitHub page.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.