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.