Rails Image Upload Using Dragonfly
Upload file là một trong những tính năng wuan trọng của một ứng dụng Web. Nó cho phép người dung tải file cá nhân lên ứng dụng Web để sử dụng vào một mục đích cá nhân nào đó. Đã cá rất nhiều gem hỗ trợ developer thực hiện tính năng này như PaperClip, CarrierWave, v.v… Ngày hôm nay, tôi xin ...
Upload file là một trong những tính năng wuan trọng của một ứng dụng Web. Nó cho phép người dung tải file cá nhân lên ứng dụng Web để sử dụng vào một mục đích cá nhân nào đó. Đã cá rất nhiều gem hỗ trợ developer thực hiện tính năng này như PaperClip, CarrierWave, v.v… Ngày hôm nay, tôi xin giới thiệu them một lựa chọn nữa cho các bạn đó là gem Dragonfly.
Dragonfly là một Ruby gem có khả năng tùy biến cao cho việc upload ảnh cũng như đính kèm file trong ứng dụng Rails.
Trong bài viết này, tôi sẽ tạo một ứng dụng Rails có tên là Dragon-Uploader, chỉ có một tính năng duy nhất là upload ảnh để minh họa cho việc sử dụng Dragonfly.
Để sử dụng Dragonfly, ta cần cài đặt ImageMagick trong máy. Để thực hiện việc này trên Ubuntu đơn giản chỉ với một câu lệnh sau:
sudo apt-get install imagemagick
rails new dragon-uploader -T
-T là tùy chọn giúp loại bỏ việc generate test cho ứng dụng.
Thêm gem dragonfly vào Gemfile
#Gemfile gem 'dragonfly', '~> 1.0', '>= 1.0.12'
Sau đó chạy bundle để cài đặt:
bundle install
Tiếp theo, ta sẽ tạo PhotosController cho ứng dụng:
rails generate controller Photos
Để sử dụng Dragonfly vào ứng dụng Rails thì điều đầu tiên cần làm là config thư viện này bằng cách thực hiện lệnh sau trong terminal:
rails generate dragonfly
Câu lệnh trên sẽ tạo ra file config dragonfly.rb trong thư mục config/initializers như sau:
#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
Sau khi đã taọ ra file config, kế đến ta sẽ tạo model Photo cho ứng dụng:
rails generate model Photo
#app/models/photo.rb class Photo < ActiveRecord::Base dragonfly_accessor :image end
Dragonfly cung cấp phương thức dragonfly_accessor được sử dụng để đọc và ghi dữ liệu vào cột đực chỉ định (Trong ví dụ trên là cột image_uid).
Bây giờ, ta cần migrate column cho bảng photos như sau:
#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
Chú ý: Nếu bạn đang sử dụng là avatar thì tên cột phải là avatar_uid
Migrate database:
rake db:migrate
Cài đặt PhotosController để có thể upload ảnh:
#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
Sau đó, ta cần config routes.rb cho các action index, new, create:
#config/routes.rb Rails.application.routes.draw do resource :photos only: [:index, :new, :create] root to: "photos#index" end
Bước tiếp theo là ta cần tạo views để hiển thị danh sách ảnh(Ở đây tôi chỉ hiển thị dạng table đơn giản):
#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>
- Và tạo form upload ảnh:
#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 %>
Để đảm bảo tính bảo mật, bạ không muốn người dung có thể upload bất kỳ loại file nào, hay giới hạn dung lượng file upload trong một lần. Tất nhiên, Drangonfly cung cấp đầy đủ các phương thức để thực hiện điều này. Ta cần khai báo để sử dụng các phương thức này.
#config/initializers/dragonfly.rb # Sử dụng các phương thức validate của Dragonfly if defined?(ActiveRecord::Base) ActiveRecord::Base.extend Dragonfly::Model ActiveRecord::Base.extend Dragonfly::Model::Validations end
Bây giờ ta thêm validate cho model Photo:
#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
Danh sách các phương thức validations của Dragonfly:
class Photo extend Dragonfly::Model::Validations validates_presence_of :image validates_size_of :image, maximum: 500.kilobytes # Kiểm tra phần mở rộng của file validates_property :ext, of: :image, as: 'jpg' # ..hoặc.. validates_property :mime_type, of: :image, as: 'image/jpeg' # Kiểm tra độ rộng của ảnh validates_property :awidth, of: :image, in: (0..400), message: "should not be more than 400px" # ..hoặc chỉ check khi image_change?(Sử dụng khi update ảnh) validates_property :format, of: :image, as: 'png', if: :image_changed? end
Bước tiếp theo, ta cài đặt thêm các action edit, show, destroy trong PhotosController để bổ sung tính năng cho phép người dùng cập nhật, xóa và xem chi tiết một ảnh như dưới đây:
#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
- Form cập nhật một ảnh
#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 %>
- Xem chi tiết một ảnh
#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 %>
Cuối cùng, ta cần thay đổi routes.rb để có thể truy cập được các chức năng vừa thêm:
#config/routes.rb Rails.application.routes.draw do resources :photos root to: "photos#index" end
Trong bài viết này, tôi đã chỉ cách sử dụng cơ bản của Dragonfly qua một ứng dụng Rails đơn giản. Để tìm hiểu thêm, cũng như sử dụng các tính năng khác của gem, các bạn tham khảo documentation
Hy vọng bài viết đã giúp ích cho mọi người (bow) !