Rails Image Upload Using Dragonfly
Bài đăng này đã không được cập nhật trong 8 năm
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
.
Cài đặt ImageMagick#
Để 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
Tạo Rails Application#
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#
Để 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 %>
Validations#
Để đả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 :width, 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
Kết luận#
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) !
All rights reserved