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.
#Gemfile 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:
#config/intializers/dragonfly.rb require 'dragonfly' # Configure Dragonfly.app.configure do plugin :imagemagick secret "e83b8affbf1c807c7788c07d27e70e79fb0459f8e2c4375b59e60a3da11631e5" url_format "/media/:job/:name" datastore :file, root_path: Rails.root.join('public/system/dragonfly', Rails.env), server_root: Rails.root.join('public') end # Logger Dragonfly.logger = Rails.logger # Mount as middleware Rails.application.middleware.use Dragonfly::Middleware # Add model functionality if defined?(ActiveRecord::Base) ActiveRecord::Base.extend Dragonfly::Model ActiveRecord::Base.extend Dragonfly::Model::Validations end
rails generate model Photo
#app/models/photo.rb class Photo < ActiveRecord::Base dragonfly_accessor :image 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.
#xxx_create_photos.rb class CreatePhotos < ActiveRecord::Migration def change create_table :photos do |t| t.string :image_uid t.string :title t.timestamps null: false end end 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:
#app/controllers/photos_controller.rb class PhotosController < ApplicationController def index @photos = Photo.all end def new @photo = Photo.new end def create @photo = Photo.new(photo_params) if @photo.save redirect_to photos_path else render :new end end private def photo_params params.require(:photo).permit(:image, :title) end end
You will need to configure your routes.
For now, add routes to the three actions you have created.
#config/routes.rb Rails.application.routes.draw do resource :photos only: [:index, :new, :create] root to: "photos#index" end
You need to set up your views as I have below:
#app/views/photos/index.html.erb <h2>Photos</h2> <p id="notice"><%= notice %></p> <table> <thead> <tr> <th>Title</th> <th>Image</th> <th colspan="3"></th> </tr> </thead> <tbody> <% @photos.each do |photo| %> <tr> <td><%= photo.title %></td> <td><%= link_to image_tag(photo.image.thumb('100x100').url), photo.image.url %></td> <td><%= link_to 'Show', photo %></td> <td><%= link_to 'Edit', edit_photo_path(photo) %></td> <td><%= link_to 'Destroy', photo, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </tbody> </table>
#app/views/photos/new.html.erb <%= form_for @photo do |f| %> <div> <%= f.label :title %> <%= f.text_field :title %> </div> <div> <%= f.label :image %> <%= f.file_field :image %> </div> <div> <%= f.submit :submit %> </div> <% 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.
#config/initializers/dragonfly.rb # Add model functionality if defined?(ActiveRecord::Base) ActiveRecord::Base.extend Dragonfly::Model ActiveRecord::Base.extend Dragonfly::Model::Validations end
Now edit your photo model to look like what I have below:
#app/models/photo.rb class Photo < ActiveRecord::Base dragonfly_accessor :image #title validation validates_presence_of :title #image validations validates_presence_of :image validates_size_of :image, maximum: 400.kilobytes, message: "should not be more than 400KB", if: :image_changed? validates_property :format, of: :image, in: ['jpeg', 'png', 'gif'], message: "the formats allowed are: .jpeg, .png, .gif", if: :image_changed? end
Here is a full list of the validations Dragonfly offers:
class Photo extend Dragonfly::Model::Validations validates_presence_of :image validates_size_of :image, maximum: 500.kilobytes # Check the file extension validates_property :ext, of: :image, as: 'jpg' # ..or.. validates_property :mime_type, of: :image, as: 'image/jpeg' # ..or actually analyse the format with imagemagick.. validates_property :format, of: :image, in: ['jpeg', 'png', 'gif'] validates_property :width, of: :image, in: (0..400), message: "é demais cara!" # ..or you might want to use image_changed? method.. validates_property :format, of: :image, as: 'png', if: :image_changed? 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:
#app/controllers/photos_controller.rb class PhotosController < ApplicationController before_action :set_photos, only: [:show, :edit, :update, :destroy] def index @photos = Photo.all end def new @photo = Photo.new end def create @photo = Photo.new(photo_params) if @photo.save redirect_to @photo else render :new end end def show end def edit end def update if @photo.update(photo_params) redirect_to @photo, notice: "photo successfully updated" else render :edit end end def destroy @photo.destroy redirect_to photos_url, notice: 'photo was successfully destroyed.' end private def photo_params params.require(:photo).permit(:image, :title) end def set_photos @photo = Photo.find(params[:id]) end end
#app/views/photos/edit.html.erb <%= form_for @photo do |f| %> <% if @photo.errors.any? %> <div id="error_explanation"> <h2><%= pluralize(@photo.errors.count, "error") %> prohibited this photo from being saved:</h2> <ul> <% @photo.errors.full_messages.each do |message| %> <li><%= message %></li> <% end %> </ul> </div> <% end %> <div> <%= f.label :title %> <%= f.text_field :title %> </div> <div> <%= f.label :image %> <%= f.file_field :image %> </div> <div> <%= f.submit :submit %> </div> <% end %> <%= link_to "Show", @photo %> | <%= link_to "Back", photos_path %>
#app/views/photos/show.html.erb <div> <strong>Title:</strong> <%= @photo.title %> </div> <div> <strong>Image:</strong> <%= image_tag @photo.image.thumb('400x200#').url if @photo.image_stored? %> </div> <%= link_to 'Edit', edit_photo_path(@photo) %> | <%= 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:
#config/routes.rb Rails.application.routes.draw do resources :photos root to: "photos#index" 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.
Comments