Gem CarrierWave

Nếu bạn muốn upload files một cách dễ dàng thì 1 giải pháp cho bạn là có thể sử dụng gem carrierWave. Gem này cung cấp một cách đơn giản và cực kỳ linh hoạt để upload các file từ từ các ứng dụng ruby. Sau đây chúng ta sẽ đi tìm hiểu về cách sử dụng gem carrierWave để upload files như thế nào

1. Cài đặt

Trong file Gemfile cần thêm vào

gem 'carrierwave'

Sau đó chạy bundle install để cài đặt và cuối cùng là restart lại server để nhận các thay đổi

2. Bắt đầu

Đầu tiên cần tạo 1 uploader, bạn có thể sử dụng rails để sinh ra nó

rails generate uploader Avatar

Sau khi chạy câu lệnh trên nó sẽ tạo ra file: app/uploaders/avatar_uploader.rb với nội dung file được tạo giống như sau

class AvatarUploader < CarrierWave::Uploader::Base
  storage :file
end

Bạn có thể sử dụng class uploader để lưu trữ và truy vấn dữ liệu như sau

uploader = AvatarUploader.new

uploader.store!(my_file)

uploader.retrieve_from_store!('my_file.png')

CarrierWave cung cấp cho bạn một store để lưu trữ lâu dài và một cache để lưu trữ tạm thời. Bạn có thể lựa chọn các store khác nhau, bao gồm cả filesystem và cloud storage

Active Record

Hãy chắc chắn rằng bạn đang load CarrierWave sau khi load ORM của bạn, nếu không bạn sẽ cần sử dụng require thủ công

require 'carrierwave/orm/activerecord'

Thêm một column vào model bằng cách tạo một migration

rails g migration add_avatar_to_users avatar:string
rake db:migrate

Mở file model của ban và thêm vào gắn kết uploader

class User < ActiveRecord::Base
  mount_uploader :avatar, AvatarUploader
end

Bây giờ bạn có thể gán file bạn muốn upload cho thuộc tính của user, chúng sẽ được tự động lưu trữ khi bản ghi được save

u = User.new
u.avatar = params[:file] # Assign a file like this, or

# like this
File.open('image_user1') do |f|
  u.avatar = f
end

u.save!
u.avatar.url # => '/url/to/file.png'
u.avatar.current_path # => 'path/to/file.png'
u.avatar_identifier # => 'file.png'

3. Upload nhiều file

Chú ý: Bạn phải chỉ rõ sử dụng nhánh master để kích hoạt tính năng này

gem 'carrierwave', github: 'carrierwaveuploader/carrierwave'

Active record

Thêm một column để có thể lưu trữ một mảng. Nó có thể là một column mảng hoặc là column json. Bạn có thể tạo 1 migration như sau

rails g migration add_avatars_to_users avatars:json
rake db:migrate

Mở file model vừa tạo được và thêm vào uploader

class User < ActiveRecord::Base
  mount_uploaders :avatars, AvatarUploader
end

Hãy chắc chắn rằng các trường nhập file của bạn được cài đặt multiple. Ví dụ

<%= form.file_field :avatars, multiple: true %>

Và đảm bảo rằng controller upload của bạn cho phép thuộc tính upload nhiều file

params.require(:user).permit(:email, :first_name, :last_name, {avatars: []})

Bây giờ bạn có thể lựa chọn nhiều files trong dialog tải lên (có thể sử dụng tổ hợp phím SHIFT+SELECT), chúng sẽ được lưu tự động khi bản ghi được save

u = User.new(params[:user])
u.save!
u.avatars[0].url # => '/url/to/image.png'
u.avatars[0].current_path # => 'path/to/image.png'
u.avatars[0].identifier # => 'image.png'

4. Thay đổi thư mục lưu trữ

Để thay đổi nơi lưu trữ file được tải lên bạn chỉ cần ghi đè lên phương thức store_dir

class MyUploader < CarrierWave::Uploader::Base
  def store_dir
    'public/my/upload/directory'
  end
end

Nếu bạn lưu trữ các file bên ngoài thư mục gốc project, bạn có thể muốn định nghĩa cache_dir

class MyUploader < CarrierWave::Uploader::Base
  def cache_dir
    '/tmp/projectname-cache'
  end
end

5. Bảo vệ upload

Một số tập tin có thể gây nguy hại nếu tải lên sai vị trí, như các file php hoặc các file mã lệnh khác. CarrierWave cho phép bạn chỉ định một danh sách được cho phép. Nếu bạn đang gắn kết uploader, tải lên 1 file lỗi làm cho các bản ghi không hợp lệ. Nêu không một lỗi sẽ được sinh ra

class MyUploader < CarrierWave::Uploader::Base
  def extension_white_list
    %w(jpg jpeg gif png)
  end
end

6. Thêm versions

Thường thì bạn sẽ muốn thêm các versions khác nhau của 1 file. Ví dụ đơn giản là thu nhỏ hình ảnh. Để làm được điều này bạn cần phải cài đặt Imagemagick và MiniMagick để thay đổi kích thước của hình ảnh

class MyUploader < CarrierWave::Uploader::Base
  include CarrierWave::MiniMagick

  process resize_to_fit: [800, 800]

  version :thumb do
    process resize_to_fill: [200,200]
  end
end

Khi upload bắt đầu, một image được tải lên và bạn muốn thay đổi kích thước của nó không vượt quá 800 x 800px. Một versions mới thumb được viết ra để thu nhỏ hình ảnh 200 x 200px. Ta có thể viết như sau

uploader = AvatarUploader.new
uploader.store!(image)                              # size: 1024x768

uploader.url # => '/url/to/image.png'               # size: 800x600
uploader.thumb.url # => '/url/to/thumb_image.png'   # size: 200x200

Bạn cũng có thể sử dụng các versions lồng nhau

class MyUploader < CarrierWave::Uploader::Base

  version :animal do
    version :human
    version :monkey
    version :llama
  end
end

Bạn cũng có thể thêm điều kiện cho các versions

class MyUploader < CarrierWave::Uploader::Base

  version :human, :if => :is_human?
  version :monkey, :if => :is_monkey?
  version :banner, :if => :is_landscape?

protected

  def is_human? picture
    model.can_program?(:ruby)
  end

  def is_monkey? picture
    model.favorite_food == 'banana'
  end

  def is_landscape? picture
    image = MiniMagick::Image.open(picture.path)
    image[:width] > image[:height]
  end

end

6. Cấu hình CarrierWave

CarrierWave có rất nhiều cấu hình mà bạn có thể tuỳ chọn, cho toàn bộ hoặc cho từng uploader

CarrierWave.configure do |config|
  config.permissions = 0666
  config.directory_permissions = 0777
  config.storage = :file
end

Hoặc là

class AvatarUploader < CarrierWave::Uploader::Base
  permissions 0777
end

Trong rails bạn cần khởi tạo cho nó

config/initializers/carrierwave.rb

7. sử dụng Amazon S3

fog được sử dụng để hỗ trợ Amazon S3. Bạn cần thêm nó vào trong Gemfile

gem "fog", "~> 1.3.1"

Bạn cần cung cấp fog_credentials và fog_directory của bạn trong initializer

CarrierWave.configure do |config|
  config.fog_credentials = {
    :provider               => 'AWS',                        # required
    :aws_access_key_id      => 'xxx',                        # required
    :aws_secret_access_key  => 'yyy',                        # required
    :region                 => 'eu-west-1',                  # optional, defaults to 'us-east-1'
    :host                   => 's3.example.com',             # optional, defaults to nil
    :endpoint               => 'https://s3.example.com:8080' # optional, defaults to nil
  }
  config.fog_directory  = 'name_of_directory'                     # required
  config.fog_public     = false                                   # optional, defaults to true
  config.fog_attributes = {'Cache-Control'=>'max-age=315576000'}  # optional, defaults to {}
end

Trong uploader bạn cần thiết lập storage cho :fog

class AvatarUploader < CarrierWave::Uploader::Base
  storage :fog
end

Bây giờ bạn có thể sử dụng phương thức CarrierWave::Uploader#url để trả lại url cho các file trên Amazon S3

Kết luận

Trên đây là những kiến thức cơ bản về gem CarrierWave. Do kiến thức hạn hẹp nên vẫn còn nhiều thiếu sót. Rất mong sự góp ý từ bạn đọc. Cảm ơn vì đã dành thời gian để theo dõi bài viết của tôi. Mong rằng nó có thể giúp ích cho bạn