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

Tải Lên Tệp Dễ Dàng Trong Rails Với Shrine: Giải Pháp Hiện Đại, Module

Có nhiều gem tải lên tệp như CarrierWave, Paperclip, Dragonfly, chỉ nennen vài cái. Chúng đều có đặc thước, và bạn có thể đã dùng ít nhất một trong số đó.

Hôm nay, tuy nhiên, tôi muốn giới thiệu một giải pháp tương đối mới nhưng rất cool gọi là Shrine, do Janko Marohnić tạo. Khác với một số gem tương tự, nó có cách tiếp cận module, nghĩa là mọi tính năng được đóng gói thành module (hoặc plugin trong thuật ngữ Shrine). Muốn hỗ trợ validation? Thêm plugin. Muốn xử lý file? Thêm plugin! Tôi thực sự thích cách này vì nó cho phép dễ dàng kiểm soát tính năng nào khả dụng cho model nào.

Trong bài này tôi sẽ chỉ cách:

  • tích hợp Shrine vào ứng dụng Rails
  • cấu hình nó (toàn cục và per-uploader)
  • thêm khả năng tải lên tệp
  • xử lý tệp
  • thêm quy tắc validation
  • lưu metadata thêm và dùng cloud storage Amazon S3

Mã nguồn bài viết có sẵn trên GitHub.

Demo hoạt động tại đây.

Tích Hợp Shrine

Khởi tạo bằng cách tạo ứng dụng Rails mới không có test suite mặc định:

Demo này dùng Rails 5, nhưng hầu hết khái niệm áp dụng cho version 3 và 4.

Thêm gem Shrine vào Gemfile:

Chạy:

Bây giờ cần model tên Photo. Shrine lưu tất cả thông tin file trong cột text đặc biệt kết thúc bằng hậu tố _data. Tạo và apply migration:

Lưu ý với Rails cũ, lệnh sau nên là:

Tùy chọn cấu hình Shrine có thể set cả toàn cục và per-model. Cài đặt toàn cục làm trong file initializer. Ở đó hook up file cần thiết và plugins. Plugin dùng trong Shrine để tách chức năng thành module riêng, cho bạn kiểm soát đầy đủ các tính năng. Ví dụ có plugin cho validation, xử lý ảnh, cache attachment, v.v.

Hiện tại thêm hai plugin: hỗ trợ ActiveRecord và setup logging. Sẽ include toàn cục. Cũng setup file system storage:

config/initializers/shrine.rb

Logger chỉ output thông tin debug ra console cho biết thời gian xử lý file. Có thể hữu ích.

Tất cả file upload sẽ lưu trong public/uploads. Không muốn track trong Git, loại thư mục này:

.gitignore

Tạo class "uploader" đặc biệt chứa cài đặt model-specific. Lúc này class rỗng:

models/image_uploader.rb

Cuối cùng, include class này vào model Photo:

models/photo.rb

[:image] thêm virtual attribute dùng khi xây form. Dòng trên viết lại được:

Tuyệt! Model giờ có chức năng Shrine, tiến bước tiếp.

Controller, Views, và Routes

Demo này chỉ cần một controller quản lý photo. Trang index làm root:

pages_controller.rb

View:

views/photos/index.html.erb

Để render mảng @photos, cần partial:

views/photos/_photo.html.erb

image_data? là method ActiveRecord cung cấp kiểm tra record có ảnh hay không.

image_url là method Shrine trả path đến ảnh gốc. Tất nhiên, hiển thị thumbnail nhỏ tốt hơn, sẽ lo sau.

Thêm routes cần thiết:

config/routes.rb

Xong—căn cơ hoàn tất, tiến phần thú vị!

Tải Lên Tệp

Phần này chỉ cách thêm chức năng upload thực sự. Controller actions đơn giản:

photos_controller.rb

Điều cần lưu ý: strong parameters phải permit virtual attribute image, không phải image_data.

photos_controller.rb

Tạo view new:

views/photos/new.html.erb

Partial form cũng đơn giản:

views/photos/_form.html.erb

Lại nhắc: dùng attribute image, không phải image_data.

Cuối cùng, thêm partial hiển thị lỗi:

views/shared/_errors.html.erb

Đây là cơ bản—bạn có thể upload ảnh ngay.

Validations

Tuy nhiên còn nhiều việc để hoàn thiện app. Vấn đề chính: user có thể upload bất kỳ loại file nào với bất kỳ size, không tốt. Do đó thêm plugin hỗ trợ validation:

config/initializers/shrine.rb

Setup logic validation cho ImageUploader:

models/image_uploader.rb

Chỉ cho upload JPG và PNG dưới 1MB. Tùy chỉnh rule theo ý.

MIME Types

Điều quan trọng: mặc định Shrine xác định MIME type file dùng Content-Type HTTP header. Header này do browser pass và set chỉ dựa trên extension file, không luôn mong muốn.

Nếu muốn xác định MIME type dựa trên nội dung file, dùng plugin determine_mime_type. Include trong class uploader vì model khác có thể không cần:

models/image_uploader.rb

Plugin này dùng Linux's file utility mặc định.

Cache Attached Images

Hiện tại, khi user submit form với data sai, form hiển thị lại với lỗi render ở trên. Vấn đề: attached image bị mất, user phải chọn lại. Rất dễ fix với plugin cached_attachment_data:

models/image_uploader.rb

Thêm hidden field vào form:

views/photos/_form.html.erb

Chỉnh Sửa Ảnh

Ảnh đã upload được, nhưng chưa sửa được, fix ngay. Controller actions tương đối đơn giản:

photos_controller.rb

Cùng partial _form:

views/photos/edit.html.erb

Chưa đủ: user vẫn không xóa ảnh upload. Để cho phép, cần—đoán gì—plugin khác:

models/image_uploader.rb

Nó dùng virtual attribute :remove_image, permit trong controller:

photos_controller.rb

Hiển thị checkbox xóa ảnh nếu record có attachment:

views/photos/_form.html.erb

Tạo Thumbnail Image

Hiện tại hiển thị ảnh gốc, không tốt cho preview: ảnh lớn chiếm diện tích. Dùng CSS width/height cũng không hay. Dù set nhỏ bằng style, user vẫn tải file gốc, có thể to.

Tốt hơn: tạo preview nhỏ server-side khi upload ban đầu. Cần hai plugin và hai gem thêm. Thêm gem:

image_processing do tác giả Shrine tạo, cung cấp helper high-level manipulate ảnh. Gem này dựa trên mini_magick, Ruby wrapper cho ImageMagick. Bạn cần ImageMagick trên hệ thống để chạy demo.

Cài gem mới:

Include plugin cùng dependency:

models/image_uploader.rb

Processing plugin cho phép manipulate ảnh (thu nhỏ, xoay, convert format...). Versions cho phép ảnh nhiều variant. Demo lưu hai version: "original" và "thumb" (resize 300x300).

Code xử lý ảnh và lưu hai version:

models/image_uploader.rb

resize_to_limit! do gem image_processing cung cấp. Thu nhỏ ảnh xuống 300x300 nếu lớn hơn, không làm gì nếu nhỏ hơn. Giữ tỷ lệ khung hình gốc.

Khi hiển thị ảnh, chỉ cần truyền :original hoặc :thumb cho method image_url:

views/photos/_photo.html.erb

Tương tự trong form:

views/photos/_form.html.erb

Để tự động xóa file processed sau upload xong, thêm plugin delete_raw:

models/image_uploader.rb

Metadata Ảnh

Ngoài render ảnh, có thể fetch metadata. Ví dụ hiển thị size và MIME type ảnh gốc:

views/photos/_photo.html.erb

Còn kích thước? Mặc định không lưu, nhưng có thể với plugin store_dimensions.

Kích Thước Ảnh

Plugin store_dimensions dựa trên gem fastimage, hook nó:

Đừng quên:

Include plugin:

models/image_uploader.rb

Hiển thị dimensions dùng method width và height:

views/photos/_photo.html.erb

Cũng có method dimensions trả array [width, height] (ví dụ [500, 750]).

Chuyển Sang Cloud

Developer thường chọn cloud service host file upload, Shrine hỗ trợ. Phần này chỉ cách upload lên Amazon S3.

Bước đầu, thêm hai gem vào Gemfile:

aws-sdk cần cho S3 SDK, dotenv-rails quản lý env variable development.

Trước khi tiếp, cần key pair truy cập S3 qua API. Đăng nhập (đăng ký) Amazon Web Services Console, vào Security Credentials > Users. Tạo user có permission manipulate file trên S3. Policy đơn cho full access S3:

Tải key pair user tạo. Hoặc dùng root access key, nhưng tôi khuyên mạnh không nên vì rất không an toàn.

Tạo S3 bucket host file, thêm file config vào root project:

.env

Tuyệt đối không expose file này công khai, chắc chắn exclude khỏi Git:

.gitignore

Sửa cấu hình toàn cục Shrine, thêm storage mới:

config/initializers/shrine.rb

Xong! Không cần thay đổi phần khác của app, test storage mới ngay. Nếu nhận lỗi S3 về key sai, chắc chắn copy key/secret chính xác, không trailing space và ký tự đặc biệt vô hình.

Kết Luận

Đến cuối bài. Hy vọng bạn tự tin dùng Shrine và háo hức dùng cho dự án. Đã thảo luận nhiều tính năng, nhưng còn hơn thế, như lưu context thêm cùng file và direct upload mechanism.

Hãy đọc tài liệu Shrine và trang chính thức, mô tả kỹ tất cả plugin. Nếu còn câu hỏi, đừng ngần ngại hỏi. Cảm ơn bạn theo dõi, hẹn gặp lại!