Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm chủ Tải lên Tệp trong Rails với CarrierWave

Đây là bài viết tiếp theo trong loạt bài "Tải lên với Rails". Hôm nay chúng ta sẽ làm quen với CarrierWave—một trong những giải pháp tải lên tệp phổ biến nhất cho Rails. Tôi thích CarrierWave vì nó dễ dàng bắt đầu, có sẵn nhiều tính năng ngay từ đầu, và cung cấp hàng chục bài viết "hướng dẫn" do cộng đồng viết, nên bạn sẽ không bị lạc lối.

Trong bài viết này, bạn sẽ học cách:

  • Tích hợp CarrierWave vào ứng dụng Rails
  • Thêm các xác thực (validations)
  • Lưu giữ tệp qua các request
  • Xóa tệp
  • Tạo ảnh thu nhỏ (thumbnails)
  • Tải lên tệp từ vị trí từ xa
  • Triển khai tải lên nhiều tệp
  • Thêm hỗ trợ lưu trữ đám mây

Mã nguồn cho bài viết này có sẵn trên GitHub. Chúc bạn đọc vui vẻ!

Đặt Nền Móng

Như thường lệ, bắt đầu bằng cách tạo một ứng dụng Rails mới:

Đối với demo này, tôi sẽ sử dụng Rails 5.0.2. Lưu ý rằng CarrierWave 1 chỉ hỗ trợ Rails 4+ và Ruby 2. Nếu bạn vẫn đang dùng Rails 3, hãy sử dụng CarrierWave phiên bản 0.11.

Để thấy CarrierWave hoạt động, chúng ta sẽ tạo một ứng dụng blog đơn giản với mô hình Post duy nhất. Nó sẽ có các thuộc tính chính sau:

  • title (string)
  • body (text)
  • image (string)—trường này sẽ chứa hình ảnh (tên tệp, chính xác hơn) đính kèm với bài viết

Tạo và áp dụng migration mới:

Thiết lập một số routes:

config/routes.rb

Cũng tạo một controller cơ bản:

posts_controller.rb

Bây giờ tạo view index:

views/posts/index.html.erb

Và partial tương ứng:

views/posts/_post.html.erb

Ở đây tôi dùng phương thức truncate của Rails để chỉ hiển thị 150 ký tự đầu tiên của bài viết. Trước khi tạo các view khác và partial form, hãy tích hợp CarrierWave vào ứng dụng trước.

Tích hợp CarrierWave

Thêm gem mới vào Gemfile:

Gemfile

Chạy:

CarrierWave lưu cấu hình bên trong các uploaders được include vào model. Để tạo uploader, dùng lệnh:

Bây giờ, trong thư mục app/uploaders, bạn sẽ thấy file image_uploader.rb. File này có các comment và ví dụ hữu ích, bạn có thể tham khảo để bắt đầu. Trong demo này chúng ta dùng ActiveRecord, nhưng CarrierWave cũng hỗ trợ Mongoid, Sequel, và DataMapper.

Tiếp theo, cần mount uploader này vào model:

models/post.rb

Uploader đã có cài đặt mặc định hợp lý, nhưng ít nhất cần chọn nơi lưu trữ tệp tải lên. Tạm thời dùng lưu trữ file:

uploaders/image_uploader.rb

Mặc định tệp sẽ nằm trong thư mục public/uploads, nên tốt nhất loại trừ nó khỏi version control:

.gitignore

Bạn cũng có thể sửa phương thức store_dir trong uploader để chọn vị trí khác.

Lúc này có thể tạo view mới và form partial để bắt đầu tải lên tệp:

views/posts/new.html.erb

views/posts/_form.html.erb

Lưu ý PostsController không cần sửa đổi vì đã permit thuộc tính image.

Cuối cùng, tạo view edit:

views/posts/edit.html.erb

Xong! Bạn có thể khởi động server và thử tạo bài viết với hình ảnh. Vấn đề là hình ảnh không hiển thị đâu, nên hãy sang phần tiếp theo để thêm trang show!

Hiển Thị Hình Ảnh

View chưa tạo duy nhất là show. Thêm ngay:

views/posts/show.html.erb

Như bạn thấy, hiển thị đính kèm rất dễ: chỉ cần @post.image.url để lấy URL hình ảnh. Để lấy đường dẫn file, dùng phương thức current_path. CarrierWave cũng cung cấp phương thức image? để kiểm tra xem có đính kèm hay không (phương thức image không bao giờ trả về nil ngay cả khi file không tồn tại).

Sau khi vào trang bài viết, bạn sẽ thấy hình ảnh, nhưng nó có thể quá lớn vì chưa giới hạn kích thước. Có thể thu nhỏ bằng CSS, nhưng tốt hơn là tạo thumbnail sau khi upload. Tuy nhiên điều này cần thêm một vài bước.

Tạo Thumbnail

Để cắt và thay đổi kích thước ảnh, cần công cụ riêng. CarrierWave hỗ trợ sẵn các gem RMagick và MiniMagick, dùng ImageMagick để thao tác ảnh. ImageMagick là giải pháp mã nguồn mở cho phép chỉnh sửa và tạo ảnh mới, nên trước tiên cần tải và cài đặt nó. Tôi chọn MiniMagick vì dễ cài đặt và hỗ trợ tốt hơn:

Gemfile

Chạy:

Include MiniMagick vào uploader:

uploaders/image_uploader.rb

Bây giờ chỉ cần thêm version mới cho uploader. Khái niệm versions (hoặc styles) dùng trong nhiều thư viện tải tệp; đơn giản nghĩa là các file thêm được tạo dựa trên file gốc với kích thước hoặc định dạng khác nhau. Thêm version tên thumb:

uploaders/image_uploader.rb

Bạn có thể tạo bao nhiêu version tùy thích, hơn nữa version có thể xây dựng dựa trên version khác:

uploaders/image_uploader.rb

Nếu đã upload một số hình ảnh, chúng sẽ không có thumbnail. Không sao, bạn có thể tạo lại từ Rails console:

Cuối cùng, hiển thị thumbnail kèm link đến ảnh gốc:

views/posts/show.html.erb

Khởi động server và quan sát kết quả!

Thêm Xác Thực (Validations)

Hiện tại upload hoạt động nhưng không validate đầu vào người dùng, điều này không tốt. Vì chỉ làm việc với ảnh, hãy whitelist các đuôi .png, .jpg, .gif:

uploaders/image_uploader.rb

Bạn cũng có thể kiểm tra content type bằng phương thức content_type_whitelist:

uploaders/image_uploader.rb

Hoặc blacklist một số loại file, ví dụ file thực thi, bằng phương thức content_type_blacklist.

Ngoài kiểm tra loại và đuôi file, hãy giới hạn dung lượng dưới 1 megabyte. Cần gem hỗ trợ validation file cho ActiveModel:

Gemfile

Cài đặt:

Thêm các validation mong muốn (cũng thêm kiểm tra cho title và body):

models/post.rb

Tiếp theo, thêm bản dịch I18n cho thông báo lỗi của CarrierWave:

config/locales/en.yml

Hiện tại chưa hiển thị lỗi validation, hãy tạo partial shared:

views/shared/_errors.html.erb

Sử dụng partial này trong form:

views/posts/_form.html.erb

Thử upload file không hợp lệ và quan sát. Sẽ hoạt động, nhưng nếu chọn file hợp lệ mà không điền title hoặc body, validation vẫn fail và hiển thị lỗi. Tuy nhiên ô file sẽ bị xóa trắng, người dùng phải chọn lại ảnh, khá bất tiện. Để khắc phục, cần thêm trường khác vào form.

Lưu Giữ File Qua Các Request

Lưu giữ file khi form hiển thị lại khá dễ. Chỉ cần thêm hidden field và permit nó trong controller:

views/shared/_form.html.erb

posts_controller.rb

Bây giờ image_cache sẽ tự động điền và ảnh không bị mất. Có thể hiển thị thumbnail để người dùng biết ảnh đã xử lý thành công:

views/shared/_form.html.erb

Xóa Hình Ảnh

Tính năng phổ biến khác là cho phép xóa file đính kèm khi chỉnh sửa record. Với CarrierWave, việc này không khó. Thêm checkbox vào form:

views/shared/_form.html.erb

Và permit thuộc tính remove_image:

posts_controller.rb

Xong! Để xóa ảnh thủ công, dùng phương thức remove_image!:

Tải Lên Từ Vị Trí Từ Xa

CarrierWave còn cung cấp tính năng thú vị: tải file từ URL. Hãy thêm trường mới và permit thuộc tính tương ứng:

views/shared/_form.html.erb

posts_controller.rb

Tuyệt vời quá! Không cần thay đổi gì thêm, có thể test ngay tính năng này!

Làm Việc Với Nhiều Upload

Giả sử muốn bài viết có nhiều đính kèm. Với cài đặt hiện tại không thể, nhưng CarrierWave hỗ trợ trường hợp này. Cần thêm trường serialized (cho SQLite) hoặc JSON (cho Postgres/MySQL). Tôi thích JSON, nên chuyển sang database adapter mới. Xóa gem sqlite3 khỏi Gemfile và thêm pg:

Gemfile

Cài đặt:

Sửa cấu hình database:

config/database.yml

Tạo database Postgres tương ứng, rồi generate và apply migration:

Nếu muốn giữ SQLite, làm theo hướng dẫn trong tài liệu CarrierWave.

Mount uploaders (lưu ý dạng số nhiều!):

model/post.rb

Tôi dùng cùng uploader cho attachments, nhưng bạn có thể tạo uploader mới với cấu hình khác.

Thêm trường file multiple vào form:

views/shared/_form.html.erb

Vì attachments chứa mảng, permit như sau:

posts_controller.rb

Cuối cùng, lặp qua attachments của bài viết và hiển thị như bình thường:

views/shared/show.html.erb

Lưu ý mỗi attachment sẽ có thumbnail như cấu hình trong ImageUploader. Tuyệt!

Sử Dụng Lưu Trữ Đám Mây

Lưu trữ file cục bộ không luôn tiện lợi/khả thi, ví dụ trên Heroku không thể lưu file tùy chỉnh. Làm sao kết hợp CarrierWave với Amazon S3? Rất dễ dàng. CarrierWave phụ thuộc gem fog-aws:

Gemfile

Cài đặt:

Tạo initializer cho CarrierWave và cấu hình cloud storage toàn cục:

config/initializers/carrierwave.rb

Có thêm các tùy chọn khác, xem trong tài liệu.

Tôi dùng gem dotenv-rails để set biến môi trường an toàn, bạn có thể chọn cách khác. Nhưng hãy chắc chắn cặp key S3 không công khai, vì nếu không ai cũng có thể upload vào bucket của bạn!

Thay dòng storage :file bằng:

uploaders/image_uploader.rb

Ngoài S3, CarrierWave còn hỗ trợ Google Storage và Rackspace. Các dịch vụ này cũng dễ thiết lập.

Kết Luận

Đến đây là hết bài viết hôm nay! Chúng ta đã bao quát các tính năng chính của CarrierWave, bạn có thể bắt đầu dùng cho dự án. Nó còn nhiều tùy chọn khác, hãy đọc tài liệu.

Nếu bị mắc chỗ nào, đừng ngần ngại đặt câu hỏi. Cũng có thể tham khảo wiki của CarrierWave, nơi chứa nhiều bài "how to" trả lời câu hỏi thường gặp.

Cảm ơn bạn đã theo dõi, và happy coding!