Uploads file with CarrierWave in rails

1.Giới thiệu

CarrierWave - một tiện ích hỗ trợ việc tải các tập tin một cách linh hoạt với nhiều khả năng và sự chỉnh sửa ,nó được trang bị khá tốt cho việc tải lên tất cả các định dạng loại tập tin .Khi xây dựng ứng dụng của riêng bạn , những hình ảnh đẹp hay những tập tin quan trọng của bạn có thể được lưu trữ ngay trên ứng dụng của bạn hoặc lưu trữ bên ngoài giống như các máy chủ Amazone hay iCloud.

2.Cài đặt

Để bắt đầu việc sử dụng CarrierWave, trong gemfile của bạn , hãy thêm gem "carrierwave" và chạy bundle install

gem 'carrierwave', '~> 1.0'

Đối với version 1.0, CarrierWave yêu cầu phiên bản Rails 4.0 trở lên và Ruby 2.0 trở lên , nếu bạn đang sử dụng Rails 3.0 thì nên sử dụng gem với phiên bản v0.11.0.

3. Xây dựng ứng dụng đơn giản cho phép upload ảnh, tập tin

CarrierWave làm việc thông qua một class của Ruby goi là Uploader vì thế chúng ta sẽ bắt đầu bằng cách chạy lệnh :

$ rails generate uploader Avatar

Sau khi chạy lệnh sẽ tạo ra file 'image_uploader.rb' (trong thư mục Uploader) ở trong thư mục app của ứng dụng :

app/uploaders/avatar_uploader.rb

Tại đây bạn có thể customize việc upload file :

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

CarrierWave cung cấp cho bạn 1 kho chứa để lưu trữ vĩnh viễn và 1 bộ nhớ cache để lưu trữ tạm thời .Bạn có thể sử dụng các kho chứa khác nhau bao gồm filesystem và cloud storage. Tiến hành xây dựng một chức năng đơn giản ,cho phép upload tập tin cho Category: Step1: Khởi tạo model Category:

rails generate model category title:string

Step2: Khởi tạo model CategoryAttachment

rails generate model category_attachment category_id:integer attachment:string

rake db:migrate

Step3: in Category.rb

class Category < ActiveRecord::Base
   has_many :category_attachments
   accepts_nested_attributes_for :category_attachments
end

in CategoryAttachment.rb


class CategoryAttachment < ActiveRecord::Base
   mount_uploader :attachment, Uploader
   belongs_to :category
end

In Category_controller.rb


def new
   @category = Category.new
   @category_attachment = @category.category_attachments.build
end

def create
   @category = Category.new(category_params)
     if @category.save
       params[:category_attachments]['attachment'].each do |a|
          @category_attachment = @category.category_attachments.create!(attachment: a)
       end
      redirect_to @category
     else
        render :new
     end
 end

 private
   def category_params
      params.require(:category).permit(:title, category_attachments_attributes: [:id, :category_id, :attachment])
   end

in views/categories/_form.html.erb

<%= form_for(@category, :html => { :multipart => true }) do |f| %>
   <div class="field">
     <%= f.label :title %><br>
     <%= f.text_field :title %>
   </div>

   <%= f.fields_for category_attachments do |p| %>
     <div class="field">
       <%= p.label :attachment %><br>
       <%= p.file_field :attachment, :multiple => true, name: "category_attachments[attachment][]" %>
     </div>
   <% end %>

   <div class="actions">
     <%= f.submit %>
   </div>
<% end %>

4 .Multiple file uploads Để sử dụng được tính năng này bạn cần add thêm gem file ở nhánh master :

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

Trong database chúng ta cần thêm một cột mà có thể lưu trữ mảng , cột này có thể là mảng hoặc dạng json tùy thuộc vào sự hỗ trợ của CSDL.Ví dụ:

rails g migration add_avatars_to_users avatars:json

Tiếp đến bạn cần chắc chắn input field của bạn là multiple file field, và giá trị permit trong controller của bạn là mảng trống trong 1 hash:

<%= form.file_field :avatars, multiple: true %>
params.require(:user).permit(:email, :first_name, :last_name, {avatars: []})

Bây giờ bạn có thể lựa chọn nhiều file upload và chờ nó được tụ động lưu trữ khi bản khi được lưu.

5.Thay đổi thư mục lưu trữ Để thay đổi được vị trí lưu trữ file upload , chúng ta cần thay đổi phương thức store_dir như ví dụ sau:

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

Bạn cũng có thể định nghìa store_dir như là nil nếu bạn muốn lưu trữ file ngay lại folder gốc trong dự án của bạn.Ngoài ra nếu bạn muốn lưu trữ chúng ở ngoài dự án , bạn có thể dùng phương thức cache_dir:

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

6.Securing uploads Một số tập tin sẽ gây nghuy hiểm nếu tải lên vị trí sai, ví dụ như các tập tin PHP hay các tập tin khác , carrierwave cho phép bạn quyết định phần mở rộng các tập tin tải lên ,nếu bạn cố gắng tải một tập tin sai thì một thông báo lỗi sẽ được đưa lên:

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

Hoặc là chỉ chấp nhận nội dung là hình ảnh

class Uploader < CarrierWave::Uploader::Base
  def content_type_whitelist
    /image\//
  end
end

Hoặc bạn có thể lập ra 1 danh sách đen để từ chỗi các loại nội dung mà bạn mong muốn , nếu muốn từ chối tập tin dạng JSON , chúng ta có thể làm như thế này :

class NoJsonUploader < CarrierWave::Uploader::Base
  def content_type_blacklist
    ['application/text', 'application/json']
  end
end

7.Filenames and unicode chars Một vấn đề về bảo mật mà bạn cần được quan tâm đó là tên của các tập tin .Theo mặc định thì CarrierWave chỉ cung cấp tên theo chữ cái tiếng anh , chữ số arabic và một vài kí tự trắng , nếu bạn muốn được hỗ trợ dạng chữ địa phương thì chúng ta cần đến phương thức sanitize_regexp :

CarrierWave::SanitizedFile.sanitize_regexp = /[^[:word:]\.\-\+]/

8.Xóa file upload CarrierWave cho phép xóa file một cách dễ dàng với việc dùng checkbox field:

<%= form_for @user, html: { multipart: true } do |f| %>
  <p>
    <label>My Avatar</label>
    <%= image_tag(@user.avatar_url) if @user.avatar? %>
    <%= f.file_field :avatar %>
  </p>

  <p>
    <label>
      <%= f.check_box :remove_avatar %>
      Remove avatar
    </label>
  </p>
<% end %>

Bằng cách gọi remove_avatar!, sau đó lưu lại:

@user.remove_avatar!
@user.save

9.Thêm version Khi ảnh được tải lên có kích thước quá lớn ,chúng ta có thể thay đổi kích cỡ của bức ảnh bằng cách dùng gem 'mini_magick'

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

  process resize_to_fit: [800, 800]

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

Tài liệu tham khảo: https://github.com/carrierwaveuploader/carrierwave