CarrierWave bạn có thực sự biết hết tất cả cách dùng

Chào các bạn, CarrierWave là một gem hỗ trợ đến upload file trong ruby. Khi tìm kiếm trên viblo bạn sẽ thấy rất nhiều bài viết được giới thiệu cũng như sử dụng. Tuy nhiên khi mình đọc thì có rất nhiều phần được ghi trong document lại không được nhắc tới và trong bài viết này mình sẽ điểm qua một số chỗ mình nghĩ sẽ hấp dẫn bạn...

  1. Cài đặt
  2. Upload file và lưu file vào model
  3. Upload nhiều file và lưu vào model.
  4. Thay đổi thư mục lưu
  5. Bảo mật khi upload
  6. Cung cấp một địa chỉ lưu file default
  7. Cấu hình
  8. Test
  9. Upload lên AWS hoặc tương tự
  10. 18n
  11. Large files
  12. Skipping ActiveRecord callbacks
  13. Vấn đề mình gặp phải => dẫn tới bài viết này

Có những phần màu đỏ mà được gạch ngang là phần này bạn hoàn toản có thể tìm kiến trên viblo có rất nhiều bài viết. Những phần màu đen gạch ngang phần này mình không chắc chắn và chưa thử trên thực tế bạn có thể đọc tại document. Và sau đây, ta cùng bắt đầu...

3. Upload nhiều file và lưu vào model

CarrierWave có hỗ trợ định nghĩa upload nhiều file 1 lúc. Tuy nhiên, trong phần này mình nghĩ chỉ có một điều cần chú ý đó là: CarrierWave sẽ lưu trong model của bạn là định dang array. Tuy nhiên không phải bất kỳ CSDL nào cũng tương thích với kiểu data loại này. VD như: mysql, PostgreSQL thì có thể tương thích với loại dữ liệu json còn SQLite thì lại không vì thế với mysql, PostgreSQL bạn hoàn toàn có thể tạo record với format:

    rails g migration add_avatars_to_users avatars:json
    rake db:migrate

Còn SQLite thi nên là:

rails g migration add_avatars_to_users avatars:string
rake db:migrate

Khi đấy trong model ta cần config sẽ khác

class User < ActiveRecord::Base
  mount_uploaders :avatars, AvatarUploader
  serialize :avatars, JSON # If you use SQLite, add this line.
end

Như đoạn code trên t thấy rằng với kiểu dữ liệu string thì bắt buộc cần thêm dòng serialize :avatars, JSON để định nghĩa dữ liệu đầu ra sẽ theo dạng serialize.

Theo như vậy thì chúng ta cần config 1 kiểu dữ liệu mà tất cả CSDL đều có để tránh trường hợp code của mình chỉ chạy trên một môi trường csdl thì mình nghĩ nên để kiểu dữ liệu String vì tất cả CSDL đều hỗ trợ.

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

Để config lại nơi lưu ta cần thay override function store_dir.

Mặc định thì khi upload file, file của chúng ta sẽ được lưu trong thư mục "public/upload" tuy nhiên bây giờ bạn lại muốn dễ dàng quản lý file hơn. Thí dụ, khi bạn muốn thư mục file về user sẽ chỉ ở thư mục của user, thư mục của sản phẩm sẽ ở thư mục của sản phẩm. Thì phần này sẽ giúp bạn cần thiết.

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

Bạn có thể định nghĩa store_dir return nil nếu như bạn muốn lưu file ở thư mục gốc.

Còn nếu bạn muốn lưu file không thuộc thư mục dự án, mà ở chỗ nào khác thì bạn cần config cache_dir.

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

6. Cung cấp một địa chỉ lưu file default

Trong nhiều trường hợp khi bạn tạo một đối tượng mà muốn có dữ liệu file default được lưu trong model, mà không phải là ở trạng thái null thì đây là cách carrierwave hỗ trợ.

class MyUploader < CarrierWave::Uploader::Base
  def default_url(*args)
    "/images/fallback/" + [version_name, "default.png"].compact.join('_')
  end
end

11. Large files

Mặc định thì CarrierWave copy 1 file 2 lần, lần đầu tiên thì file sẽ được copy vào cache, lần 2 sẽ được copy vào nơi lưu trữ. Vì vậy, với 1 file lớn chúng ta sẽ mất nhiều thời gian cho việc lưu trữ. Vì thế, bạn có thể change hành động này bằng cách overriding 2 function move_to_cache vs move_to_store.

class MyUploader < CarrierWave::Uploader::Base
  def move_to_cache
    true
  end

  def move_to_store
    true
  end
end

12. Skipping ActiveRecord callbacks

Mặc định khi gắn uploader vào active record thì nghĩa là chúng ta đã thêm các callbacks vào trong model.

class User
  mount_uploader :avatar, AvatarUploader
end

=>

class User

  ................................
  
   after_save :store_avatar!
   before_save :write_avatar_identifier
   after_commit :remove_avatar!, on: :destroy
   after_commit :mark_remove_avatar_false, on: :update
   after_save :store_previous_changes_for_avatar
   after_commit :remove_previously_stored_avatar, on: :update
end

Nếu bạn muốn bỏ qua bất kỳ callbacks nào bạn có thể sử dụng phương thức skip_callback của ActiveRecord.

class User
  mount_uploader :avatar, AvatarUploader
  skip_callback :commit, :after, :remove_previously_stored_avatar
end

13. Vấn đề mình gặp phải => dẫn tới bài viết này

Bài viết này mình nghĩ rằng nên viết vì mình đã mắc sai lầm khi chưa thực sự hiểu về CarrierWave vì mình gặp được 1 yêu cầu: Khi mình tạo ra 1 file ảnh bằng code => lưu file này vào vị trí "X" nào đó => lấy ra file vào thời điểm nào đó => có thể lấy lại tất cả file đã lưu. Code của mình được deploy ở nhiều server tuy nhiên khi tạo ảnh mình chỉ muốn lưu ở trong serve của mình và chỗ nào thuộc serve cũng gọi được.

Tuy nhiên, do trước đây mình chưa tìm hiểu kỹ hết về CarrierWave nên thời điểm làm đã gặp sai lầm.

Kết luận

Hi vọng bài viết sẽ hữu ích cho bạn !!

Thanks for your reading!


All Rights Reserved