Các cách để lưu dimension khi upload file ảnh (rails + carrierwave)
Bài đăng này đã không được cập nhật trong 5 năm
Với các bài toán lưu dữ liệu là file ảnh, đôi khi chúng ta sẽ cần lưu thuộc tính dimension(widthxheight) của ảnh. Với rails + carrierwave, mình sẽ chỉ cho các bạn 3 cách để lưu thuộc tính này:
- Cách 1: Sử dụng những tính năng sẵn có của
Carrierwave - Cách 2: Sử dụng
gem image-size - Cách 3: Sử dụng
gem fast-image
Cách 1: Sử dụng những tính năng sẵn có của Carrierwave
Đầu tiên, hãy tạo 1 ứng dụng rails cơ bản như trong bài viết này của mình : Bài viết trước .
Bạn chỉ cần chú ý vào file model, controllers và uploaders
class CreateArticles < ActiveRecord::Migration[6.0]
def change
create_table :articles do |t|
t.string :title
t.string :thumbnail
t.string :dimension
t.timestamps
end
end
end
class Article < ApplicationRecord
mount_uploader :thumbnail, ImageUploader
validates :title, presence: true
end
class ArticlesController < ApplicationController
def new
@article = Article.new
end
def create
@article = Article.new article_params
if @article.save
redirect_to article_path(@article)
else
render :new
end
end
def show
@article = Article.find_by id: params[:id]
end
private
def article_params
params.require(:article).permit :thumbnail, :title, :thumbnail_cache
end
end
class ImageUploader < CarrierWave::Uploader::Base
storage :file
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
end
Để thực hiện lưu attributes dimension, mình sẽ sử dụng tính năng của MiniMagick(hoặc RMagick tùy vào trình xử lý ảnh mà bạn cài đặt). Mình tạo một process store_dimension trong file uploader như sau:
class ImageUploader < CarrierWave::Uploader::Base
include CarrierWave::MiniMagick
def store_dir
"uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
process :store_dimensions
private
def store_dimensions
if file && model
model.dimension = ::MiniMagick::Image.open(file.file)[:dimensions].join "x"
end
end
end
Mình sẽ debug vào uploader để các bạn hiểu các biến model, file ở đây là gì?
11:
12: def store_dimensions
13: byebug
=> 14: if file && model
15: model.dimension = ::MiniMagick::Image.open(file.file)[:dimensions].join "x"
16: end
17: end
18: end
(byebug) file
#<CarrierWave::SanitizedFile:0x00007f1d4427bad8 @file="/home/sun/environment/input_picture_demo/tmp/1574906055-7401-0001-8476/vai.jpg", @original_filename=nil, @content_type="image/jpeg">
(byebug) model
#<Article id: nil, title: nil, thumbnail: nil, dimension: nil, created_at: nil, updated_at: nil>
(byebug) model.dimension = ::MiniMagick::Image.open(file.file)[:dimensions].join "x"
"660x485"
Khá đơn giản và dễ hiểu đúng không? Mình sẽ tiếp tục với cách số 2.
Cách 2: Sử dụng gem image-size
Với cách số 1, các file ở định dạng đặc biệt như .swf, .tiff, .webp, .... sẽ không thể lấy được dimension. Vì vậy, mình sẽ sử dụng gem image-size để lấy được dimension cho nhiều định dạng file hơn.
Đầu tiên các bạn cần cài đặt gem này trong Gemfile :
gem 'image_size', '~> 2.0'
Và cũng tạo process store_dimension tương tự:
def store_dimensions
if file && Settings.image_extensions.include?(file.content_type) && model
image_size = ImageSize.path file.path
model.dimension = "#{image_size.width}x#{image_size.height}"
end
end
Cách 3: Sử dụng gem fast-image
So với image-size, gem fast-image mạnh mẽ hơn trong việc mở ảnh gà get size từ 1 url public.
FastImage will follow up to 4 HTTP redirects to get the image.
Cách sử dụng cũng tương đối đơn giản, sau khi cài đặt gem:
gem 'fastimage'
Ta cũng làm 1 process store_dimension :
def store_dimensions
if file && Settings.image_extensions.include?(file.content_type) && model
image_size = FastImage.size file.path
model.dimension = "#{image_size.width}x#{image_size.height}"
end
end
Ngoài ra, bạn có thể lấy trực tiếp size từ 1 public url, thay vì phải tạo process thông qua uploader:
FastImage.size("http://stephensykes.com/images/ss.com_x.gif")
Đơn giản phải không.
Bài viết của mình đến đây là kết thúc, hy vọng nó hữu ích với các bạn.
References:
https://github.com/sdsykes/fastimage
https://github.com/carrierwaveuploader/carrierwave/wiki/How-to:-Get-image-dimensions
All rights reserved