Multiple Images Uploading With CarrierWave and Mysql Array
Bài đăng này đã không được cập nhật trong 8 năm
Trong quá trình thực hiện dự án, tôi có nhận được một yêu cầu cần thực hiện việc upload nhiều ảnh cho một bài viết, đồng thời khách hàng cũng đã giới hạn số ảnh tối đa có thể cho một bài viết chỉ là 5.
Sau quá trình Google thần chưởng tôi biết được gem CarrierWave
có cho phép việc upload nhiều ảnh kết hợp với Mysql Array
Sau đây tôi sẽ minh họa tính năng trên thông qua một ứng dụng gallery đơn giản. Các tính năng trong ứng dụng này là:
- Tạo một gallery để upload ảnh
- Upload nhiều ảnh vào một gallery sẵn có
- Xóa một ảnh bất kỳ trong gallery
Các bước thực hiện chi tiết như sau:
Bước 1: Khởi tạo ứng dụng Rails#
Đầu tiên phải chạy lệnh rails new sample-gallery-app-with-carrierwave
để generate ứng dụng rails. Tiếp theo vào trong Gemfile xóa gem sqlite3
và bổ sung các gem sau:
gem "mysql2"
gem "slim-rails"
gem "carrierwave", :github => "carrierwaveuploader/carrierwave"
Tiếp theo ta tiến hành config trong file config/database.yml
để kết nối đến mysql server. Cuối cùng là chạy lệnh bundle
để sử dụng tất cả các gem đã khai báo.
Bước 2: Config CarrierWave#
Tạo file theo đường dẫn config/initializers/carrier_wave.rb
có nội dung như dưới đây để config CarrierWave :
CarrierWave.configure do |config|
config.storage :file
end
Sau đó bạn cần chạy lệnh rails generate uploader Image
để tạo một file mới theo đường dẫn app/uploaders/image_uploader.rb
Bước 3: Khởi tạo model galllery
Bây giờ, chúng ta đã có ImageUploader
, sau đây chúng ta cần viết các action CRUD cơ bản cho gallery.
Chạy lệnh rails g scaffold gallery title:string
để tạo ra views, controllers, và model gallery. Tiếp theo, ta bổ sung dòng mount_uploaders :images, ImageUploader
vào model gallery.
class Gallery < ActiveRecord::Base
# Chú ý là hàm mount_uploaders khác với hàm mount_uploader mọi người thường dùng
mount_uploaders :images, ImageUploader # mount the uploaders
end
Tiếp theo, ta cần tạo cột images vào bảng gallery bằng cách gọi lệnh sau:
rails g migration AddImagesToGallery
Trong file migration viết như sau:
class AddImagesToGallery < ActiveRecord::Migration
def change
add_column :galleries, :images, :string, array: true
end
end
Cuối cùng chạy lệnh rake db:migrate
là ta đã có một cột images kiểu array.
Bước 4: Cài đặt chức năng upload ảnh khi tạo gallery#
Sửa file app/views/galleries/_form.html.slim
để upload được nhiều ảnh trong form
.field
= f.file_field :images, multiple: true
Toàn bộ file app/views/galleries/_form.html.slim
sau khi sửa là:
= form_for @gallery do |f|
- if @gallery.errors.any?
#error_explanation
h2 = "#{pluralize(@gallery.errors.count, "error")} prohibited this gallery from being saved:"
ul
- @gallery.errors.full_messages.each do |message|
li = message
.field
= f.label :title
= f.text_field :title
.field
= f.file_field :images, multiple: true
.actions = f.submit
Sau đó, ta thay đổi là gallery_params
trong GalleriesController
để cho phép params kiểu array như sau:
def gallery_params
params.require(:gallery).permit(:title, {images: []})
end
Tiếp đến, ta thêm hiển thị ảnh vào trang show
và index
. Thêm những đoạn code sau vào file app/views/galleries/index.html.slim
:
td
- gallery.images.each do |image|
= image_tag(image.url)
Tương tự, ta cũng thêm những đoạn code sau vào file app/views/galleries/show.html.slim
:
div
- @gallery.images.each do |image|
= image_tag(image.url)
Đến đây ta đã hoàn thành xong việc tạo mới một gallery, chúng ta có thể test theo địa chỉ trên local là http://localhost:3000/galleries/new
. Sau khi tạo xong sẽ được dẫn về trang hiển thị danh sách gallery http://localhost:3000/galleries
.
Bước 5: Cài đặt chức năng add thêm ảnh cho một gallery#
Đầu tiên, ta phải định nghĩa trong file config/routes.rb
thành nội dung như sau:
Rails.application.routes.draw do
resources :galleries do
resources :images, only: :create
end
end
Tiếp theo ta tạo ImagesController
bằng cách chạy lệnh rails generate controller Images
Sau đó, ta viết code trong action create để thêm ảnh vào một gallery đã có sẵn như sau:
class ImagesController < ApplicationController
before_action :set_gallery
def create
add_more_images(images_params[:images]))
flash[:error] = "Failed uploading images" unless @gallery.save
redirect_to :back
end
private
def set_gallery
@gallery = Gallery.find(params[:gallery_id])
end
def add_more_images(new_images)
images = @gallery.images # copy images cũ
images += new_images # Thêm image mới vào mảng images
@gallery.assign_attributes(images: images) # gán lại vào thuộc tính images
end
def images_params
params.require(:gallery).permit({images: []})
end
end
Bước tiếp theo ta cần có view để thêm ảnh vào một gallery bằng cách bổ sung đoạn code sau vào trong views show của gallery(Nằm trong file app/views/galleries/show.html.slim
)
h1 Add more images
= form_for @gallery, url: gallery_images_path(@gallery), method: :post do |f| # use customized url endpoint
.field
= f.file_field :images, multiple: true
.actions = f.submit "Add More Images"
Bây giờ khi vào trang show chi tiết của bất kỳ gallery nào cũng sẽ có thêm một form để thêm ảnh vào gallery hiện tại
Bước 6 Cài đặt chức năng xóa ảnh#
Tính năng cuối cùng của ứng dụng này là xóa một image bất kỳ.
Đầu tiên, ta khai báo thêm action destroy trong file config/routes.rb
:
Rails.application.routes.draw do
resources :galleries do
resources :images, only: [:create, :destroy]
end
end
Sau đó, ta viết code cho chức năng xóa image trong action destroy trong ImagesController
như sau:
class ImagesController < ApplicationController
before_action :set_gallery
def create
add_more_images(images_params[:images])
flash[:error] = "Failed uploading images" unless @gallery.save
redirect_to :back
end
def destroy
remove_image_at_index(params[:id].to_i)
flash[:error] = "Failed deleting image" unless @gallery.save
redirect_to :back
end
private
def set_gallery
@gallery = Gallery.find(params[:gallery_id])
end
def add_more_images(new_images)
images = @gallery.images
images += new_images
@gallery.assign_attributes images: images
end
def remove_image_at_index(index)
remain_images = @gallery.images # copy images
remain_images.delete_at(index) # delete image theo index
@gallery.assign_attributes images: remain_images
end
def images_params
params.require(:gallery).permit({images: []})
end
end
Cuối cùng, ta thêm một link delete cho mỗi ảnh được hiển thị trong trang show chi tiết của một gallery như sau:
div
- @gallery.images.each_with_index do |image, index| #grab the index
div
= image_tag(image.url)
= link_to "Delete", gallery_image_path(@gallery, index), method: :delete,
data: { confirm: "Are you sure you want to delete this image?" }
Bây giờ, khi ta vào trang show của một gallery đã có đầy đủ các tính năng thêm ảnh và xóa một ảnh bất kỳ.
Lưu ý#
Trong bài viết trên tôi chỉ sử dụng CarrierWave
để upload ảnh và lưu trữ file trên local. Nếu muốn upload ảnh lên Amazon S3 thì chúng ta cần sửa lại file config của CarrierWave
như sau:
CarrierWave.configure do |config|
config.storage :fog
config.fog_credentials = {
provider: "AWS",
aws_access_key_id: ENV["AWS_ACCESS_KEY_ID"],
aws_secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
region: ENV["AWS_REGION"],
path_style: true
}
config.fog_directory = ENV['S3_BUCKET_NAME']
config.fog_public = Settings.carrierwave.fog_public
config.fog_attributes = {
"Cache-Control" => "max-age=#{eval(Settings.carrierwave.fog_cache_control).to_i}"
}
config.asset_host = ENV["CLOUDFRONT_DOMAIN_NAME"]
end
Tiếp theo cần sử dụng thêm gem "fog-aws"
trong Gemfile
để không bị lỗi uninitialized constant CarrierWave::Storage::Fog
Kết luận#
Trong bài viết trên, tôi đã hoàn thành việc hướng dẫn sử dụng `CarrierWave và mysql array để upload nhiều ảnh và lưu trữ vào cùng một cột trong MYSQL.
Như vậy, thay vì phải tạo một bảng images riêng để lưu trữ thông tin ảnh thì nay khi biết chính xác số ảnh tối đa có thể upload ta chỉ cần một cột để lưu trữ danh sách ảnh đó.
All rights reserved