Advertisement
  1. Code
  2. Ruby
  3. Ruby on Rails

Rails Image Upload Using Dragonfly

Scroll to top

File uploading is an important feature in web applications. Aside from enabling users to upload profile pictures, the use of file uploading features varies. I have shown you how to enable file uploading in your Rails application using different gems. Today I will be showing you how to do the same using Dragonfly.

Dragonfly is a highly customizable Ruby gem for handling images and other attachments and is already in use on thousands of websites.

You may be given a task to enable file uploading in a Rails application and may not want to make use of the other gems that are out there. You can give Dragonfly a shot, and you will definitely not regret it.

In this tutorial you will create a simple Rails application; I named mine Dragon-Uploader. The application will have just one feature: image uploading.

Installing ImageMagick

To use dragonfly, you need ImageMagick installed on your machine. Follow any of the steps below, depending on your operating system.

Mac Users:

brew install imagemagick

Ubuntu users:

sudo apt-get install imagemagick

Rails Application Generation

rails new dragon-uploader -T

The -T option ensures that your Rails application is generated without the default testing suite.

Go to your Gemfile and add the dragonfly gem.

1
#Gemfile
2
3
gem 'dragonfly', '~> 1.0', '>= 1.0.12'

Do not forget to bundle.

bundle install

Let's generate our controller.

rails generate controller Photos

Integrating Dragonfly

The first step to integrating Dragonfly into your Rails application is to run the dragonfly generation command from your terminal.

rails generate dragonfly

This will create an initializer file for Dragonfly in your config/initializers folder.

The file looks like this:

1
#config/intializers/dragonfly.rb
2
3
require 'dragonfly'
4
5
# Configure
6
Dragonfly.app.configure do
7
  plugin :imagemagick
8
9
  secret "e83b8affbf1c807c7788c07d27e70e79fb0459f8e2c4375b59e60a3da11631e5"
10
11
  url_format "/media/:job/:name"
12
13
  datastore :file,
14
    root_path: Rails.root.join('public/system/dragonfly', Rails.env),
15
    server_root: Rails.root.join('public')
16
end
17
18
# Logger
19
Dragonfly.logger = Rails.logger
20
21
# Mount as middleware
22
Rails.application.middleware.use Dragonfly::Middleware
23
24
# Add model functionality
25
if defined?(ActiveRecord::Base)
26
  ActiveRecord::Base.extend Dragonfly::Model
27
  ActiveRecord::Base.extend Dragonfly::Model::Validations
28
end

rails generate model Photo

1
#app/models/photo.rb
2
3
class Photo < ActiveRecord::Base
4
  dragonfly_accessor :image
5
end

Dragonfly provides an accessor that you will need to add to your model. With this you can read and write images.

Now navigate to your migration file and add columns.

1
#xxx_create_photos.rb
2
3
class CreatePhotos < ActiveRecord::Migration
4
  def change
5
    create_table :photos do |t|
6
      t.string :image_uid
7
      t.string :title
8
9
      t.timestamps null: false
10
    end
11
  end
12
end

Note: If you are making use of avatar and not image as I did above, you should change the column to avatar_uid.

Migrate your database:

rake db:migrate

Set up your PhotosController with the necessary actions to upload an image. It should look like this:

1
#app/controllers/photos_controller.rb
2
3
class PhotosController < ApplicationController
4
  def index
5
    @photos = Photo.all
6
  end
7
8
  def new
9
    @photo = Photo.new
10
  end
11
12
  def create
13
    @photo = Photo.new(photo_params)
14
    if @photo.save
15
      redirect_to photos_path
16
    else
17
      render :new
18
    end
19
  end
20
21
  private
22
23
  def photo_params
24
    params.require(:photo).permit(:image, :title)
25
  end
26
end

You will need to configure your routes.

For now, add routes to the three actions you have created.

1
#config/routes.rb
2
3
Rails.application.routes.draw do
4
  resource :photos only: [:index, :new, :create]
5
6
  root to: "photos#index"
7
end

You need to set up your views as I have below:

1
#app/views/photos/index.html.erb
2
3
<h2>Photos</h2>
4
5
<p id="notice"><%= notice %></p>
6
7
<table>
8
  <thead>
9
    <tr>
10
      <th>Title</th>
11
      <th>Image</th>
12
      <th colspan="3"></th>
13
    </tr>
14
  </thead>
15
16
  <tbody>
17
    <% @photos.each do |photo| %>
18
      <tr>
19
        <td><%= photo.title %></td>
20
        <td><%= link_to image_tag(photo.image.thumb('100x100').url), photo.image.url %></td>
21
        <td><%= link_to 'Show', photo %></td>
22
        <td><%= link_to 'Edit', edit_photo_path(photo) %></td>
23
        <td><%= link_to 'Destroy', photo, method: :delete, data: { confirm: 'Are you sure?' } %></td>
24
      </tr>
25
    <% end %>
26
  </tbody>
27
</table>
1
#app/views/photos/new.html.erb
2
3
<%= form_for @photo do |f| %>
4
  <div>
5
    <%= f.label :title %>
6
    <%= f.text_field :title %>
7
  </div>
8
  <div>
9
    <%= f.label :image %>
10
    <%= f.file_field :image %>
11
  </div>
12
  <div>
13
    <%= f.submit :submit %>
14
  </div>
15
<% end %>

We will come back to these views later.

Validations

For security purposes, you do not want to grant your users the privilege of uploading files of any type. Dragonfly provides you with the necessary methods for this in your initializers.

1
#config/initializers/dragonfly.rb
2
3
# Add model functionality
4
if defined?(ActiveRecord::Base)
5
  ActiveRecord::Base.extend Dragonfly::Model
6
  ActiveRecord::Base.extend Dragonfly::Model::Validations
7
end

Now edit your photo model to look like what I have below:

1
#app/models/photo.rb
2
3
class Photo < ActiveRecord::Base
4
  dragonfly_accessor :image
5
6
  #title validation
7
  validates_presence_of :title
8
9
  #image validations
10
  validates_presence_of :image
11
  validates_size_of :image, maximum: 400.kilobytes,
12
                    message: "should not be more than 400KB", if: :image_changed?
13
14
  validates_property :format, of: :image, in: ['jpeg', 'png', 'gif'],
15
                      message: "the formats allowed are: .jpeg, .png, .gif", if: :image_changed?
16
end

Here is a full list of the validations Dragonfly offers:

1
class Photo
2
  extend Dragonfly::Model::Validations
3
4
  validates_presence_of :image
5
  validates_size_of :image, maximum: 500.kilobytes
6
7
  # Check the file extension
8
  validates_property :ext, of: :image, as: 'jpg'
9
  # ..or..
10
  validates_property :mime_type, of: :image, as: 'image/jpeg'
11
  # ..or actually analyse the format with imagemagick..
12
  validates_property :format, of: :image, in: ['jpeg', 'png', 'gif']
13
14
  validates_property :width, of: :image, in: (0..400), message: "é demais cara!"
15
16
  # ..or you might want to use image_changed? method..
17
  validates_property :format, of: :image, as: 'png', if: :image_changed?
18
end

You can read more about it in the Dragonfly documentation.

You should also consider giving your users the option to edit their saved images. To do this, we need to add two action methods to our PhotosController and create an edit page in our views. You might want to add the delete and show action while you're at it, as I have below:

1
#app/controllers/photos_controller.rb
2
3
class PhotosController < ApplicationController
4
  before_action :set_photos, only: [:show, :edit, :update, :destroy]
5
6
  def index
7
    @photos = Photo.all
8
  end
9
10
  def new
11
    @photo = Photo.new
12
  end
13
14
  def create
15
    @photo = Photo.new(photo_params)
16
    if @photo.save
17
      redirect_to @photo
18
    else
19
      render :new
20
    end
21
  end
22
23
  def show
24
  end
25
  
26
  def edit
27
  end
28
29
  def update
30
    if @photo.update(photo_params)
31
      redirect_to @photo, notice: "photo successfully updated"
32
    else
33
      render :edit
34
    end
35
  end
36
37
  def destroy
38
    @photo.destroy
39
    redirect_to photos_url, notice: 'photo was successfully destroyed.'
40
  end
41
42
  private
43
44
  def photo_params
45
    params.require(:photo).permit(:image, :title)
46
  end
47
48
  def set_photos
49
    @photo = Photo.find(params[:id])
50
  end
51
end
1
#app/views/photos/edit.html.erb
2
3
<%= form_for @photo do |f| %>
4
  <% if @photo.errors.any? %>
5
    <div id="error_explanation">
6
      <h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2>
7
8
      <ul>
9
      <% @photo.errors.full_messages.each do |message| %>
10
        <li><%= message %></li>
11
      <% end %>
12
      </ul>
13
    </div>
14
  <% end %>
15
  <div>
16
    <%= f.label :title %>
17
    <%= f.text_field :title %>
18
  </div>
19
  <div>
20
    <%= f.label :image %>
21
    <%= f.file_field :image %>
22
  </div>
23
  <div>
24
    <%= f.submit :submit %>
25
  </div>
26
<% end %>
27
28
<%= link_to "Show", @photo %> | 
29
<%= link_to "Back", photos_path %>
1
#app/views/photos/show.html.erb
2
3
<div>
4
  <strong>Title:</strong>
5
  <%= @photo.title %>
6
</div>
7
<div>
8
  <strong>Image:</strong>
9
  <%= image_tag @photo.image.thumb('400x200#').url if @photo.image_stored? %>
10
</div>
11
12
<%= link_to 'Edit', edit_photo_path(@photo) %> |
13
<%= link_to 'Back', photos_path %>

If you try to access the show or edit page, you will be presented with errors. This is because we restricted the route to :new, :index, and :update. Now go ahead and change that; it should look like this:

1
#config/routes.rb
2
3
Rails.application.routes.draw do
4
  resources :photos
5
6
  root to: "photos#index"
7
end

Conclusion

At this point, you can now integrate Dragonfly into your Rails application. Be sure to check out the documentation if you want to try more features not mentioned here. I hope you enjoyed it.

Remember, you can always add feedback, questions, and comments in the form below.

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.