Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Ruby on Rails

Tải lên Tập tin Với Rails và Shrine

by
Difficulty:IntermediateLength:LongLanguages:

Vietnamese (Tiếng Việt) translation by Dai Phong (you can also view the original English article)

Có rất nhiều gem để tải lên tập tin, có thể kể vài cái tên như CarrierWave, Paperclip, và Dragonfly. Tất cả chúng đều có những điểm riêng, và có lẽ bạn đã sử dụng ít nhất một trong những gem này.

Tuy nhiên, hôm nay, tôi muốn giới thiệu một giải pháp tương đối mới, nhưng cũng rất tuyệt gọi là Shrine, được tạo ra bởi Janko Marohnić. Ngược lại với một số gem tương tự khác, nó có một cách tiếp cận mô-đun hoá, có nghĩa là mọi tính năng được đóng gói như một mô-đun (hoặc plugin theo thuật ngữ của Shrine). Bạn có muốn hỗ trợ việc kiểm tra? Hãy thêm một plugin. Bạn muốn xử lý một số tập tin ư? Hãy thêm môt plugin! Tôi thật sự thích cách tiếp cận này vì nó cho phép bạn dễ dàng kiểm soát các tính năng sẽ có mặt trong model nào.

Trong bài viết này, tôi sẽ hướng dẫn cho bạn cách:

  • tích hợp Shrine vào một ứng dụng Rails
  • cấu hình nó (toàn cục và từng model)
  • thêm khả năng tải tập tin lên
  • xử lý các tập tin
  • thêm các quy tắc kiểm tra
  • lưu trữ metadata (siêu dữ liệu) bổ sung và sử dụng bộ nhớ lưu trữ tập tin trên đám mây với Amazon S3

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

Bản demo có thể được tìm thấy ở đây.

Tích hợp Shrine

Để khởi động, hãy tạo một ứng dụng Rails mới mà không có bộ kiểm thử mặc định (test suite):

Tôi sẽ sử dụng Rails 5 cho bản demo này, nhưng hầu hết các khái niệm cũng có thể áp dụng cho các phiên bản 3 và 4.

Thêm gem Shrine vào Gemfile của bạn:

Sau đó chạy:

Bây giờ chúng ta sẽ require (yêu cầu) một model mà tôi sẽ gọi là Photo. Shrine lưu trữ tất cả các thông tin liên quan đến tập tin trong một cột kiểu text đặc biệt kết thúc với một hậu tố _data. Tạo và áp dụng migration (di trú) tương ứng:

Lưu ý rằng đối với các phiên bản cũ của Rails, lệnh thứ hai sẽ là:

Các tùy chọn cấu hình cho Shrine có thể được thiết lập cho cả trên toàn cục và từng model. Cài đặt toàn cục tất nhiên được thực hiện bên trong tập tin khởi tạo. Ở đó tôi sẽ móc nối các tập tin và plugin cần thiết. Các plugin được sử dụng trong Shrine để trích xuất các mẩu tính năng thành các mô-đun riêng biệt, cho phép bạn kiểm soát hoàn toàn các tính năng sẵn có. Ví dụ, có các plugin để kiểm tra tính hợp lệ, xử lý hình ảnh, lưu các tập tin đính kèm vào bộ nhớ đệm và vân vân.

Còn bây giờ, hãy thêm hai plugin: một để hỗ trợ ActiveRecord và một cái khác để thiết lập đăng nhập. Chúng sẽ được bao gồm trên toàn cục. Ngoài ra, thiết lập hệ thống lưu trữ tập tin:

config/initializers/shrine.rb

Logger (Trình ghi nhật ký) đơn giản sẽ xuất ra một số thông tin gỡ lỗi bên trong console cho bạn nói rằng mất bao lâu để xử lý một tập tin. Điều này có thể có ích.

Tất cả các tập tin đã được tải lên sẽ được lưu trữ trong thư mục public/uploads. Tôi không muốn theo dõi các tập tin này trong Git, vì vậy hãy loại trừ thư mục này:

.gitignore

Bây giờ hãy tạo một lớp "uploader" đặc biệt sẽ lưu trữ các cài đặt cho model cụ thể. Còn bây giờ, lớp này sẽ rỗng:

models/image_uploader.rb

Cuối cùng, hãy bao gồm lớp này bên trong model Photo:

models/photo.rb

[:image] thêm một thuộc tính ảo sẽ được sử dụng khi cấu trúc một form. Dòng trên có thể được viết lại như sau:

Tuyệt! Bây giờ model đã được trang bị tính năng của Shrine, và chúng ta có thể tiến hành bước tiếp theo.

Controller, View, và Route

Với mục đích của demo này, chúng ta chỉ cần một controller để quản lý các tấm ảnh. Trang index sẽ đóng vai trò như là gốc:

pages_controller.rb

View:

views/photos/index.html.erb

Để kết xuất mảng @photos, một phần riêng được yêu cầu:

views/photos/_photo.html.erb

image_data? là một phương thức được đưa ra bởi Shrine để kiểm tra liệu một bản ghi có chứa một hình ảnh hay không.

image_url là một phương thức khác của Shrine đơn giản trả về một đường dẫn đến hình ảnh ban đầu. Tất nhiên, tốt hơn là hiển thị một thumbnail (hình thu nhỏ), nhưng chúng ta sẽ làm việc với nó sau.

Thêm tất cả các route (tuyến) cần thiết:

config/routes.rb

Xong rồi—phần việc cơ bản đã hoàn tất, và chúng ta có thể tiến hành phần thú vị!

Tải lên Tập tin

Trong phần này, tôi sẽ hướng dẫn cho bạn cách thêm tính năng để thật sự tải lên các tập tin. Các hành động của controller rất đơn giản:

photos_controller.rb

Điều duy nhất mà tôi nắm bắt được đó là đối với các tham số mạnh bạn phải cho phép thuộc tính ảo image, chứ không phải image_data.

photos_controller.rb

Tạo view new:

views/photos/new.html.erb

Một phần của form cũng bình thường:

views/photos/_form.html.erb

Một lần nữa, xin hãy lưu ý rằng chúng ta đang sử dụng thuộc tính image chứ không phải là image_data.

Cuối cùng, thêm một phần khác để hiển thị lỗi:

views/shared/_errors.html.erb

Cũng gần xong rồi—bạn có thể bắt đầu tải lên hình ảnh ngay bây giờ.

Kiểm tra

Tất nhiên, cần phải làm thêm nhiều việc để hoàn thành ứng dụng demo. Vấn đề chính là người dùng hoàn toàn có thể tải lên bất kỳ loại tập tin nào với kích thước bất kỳ, điều mà không được tốt lắm. Vì vậy, hãy thêm một plugin để hỗ trợ xác nhận:

config/inititalizers/shrine.rb

Thiết lập logic xác nhận cho ImageUploader:

models/image_uploader.rb

Tôi chỉ cho phép tải lên các hình ảnh có định dạng JPG và PNG dưới 1MB. Hãy tinh chỉnh các quy tắc này nếu bạn thấy phù hợp.

Kiểu MIME

Một điều quan trọng cần lưu ý đó là, mặc định, Shrine sẽ xác định kiểu MIME của một tập tin bằng header Content-Type của HTTP. Header này được truyền bởi trình duyệt và thiết lập chỉ dựa trên phần mở rộng của tập tin, điều này không phải lúc nào cũng như mong đợi.

Nếu bạn muốn xác định kiểu MIME dựa trên nội dung của tập tin, hãy sử dụng plugin có tên là determ_mime_type. Tôi sẽ bao gồm nó bên trong lớp uploader, vì các model khác có thể không yêu cầu tính năng này:

models/image_uploader.rb

Mặc định plugin này sẽ sử dụng tiện ích file của Linux.

Lưu các Hình ảnh Đính kèm vào Bộ nhớ Đệm

Hiện tại, khi người dùng gửi một form có dữ liệu không chính xác, thì form sẽ được hiển thị lại với các lỗi được hiển thị ở trên. Tuy nhiên, vấn đề là hình ảnh đính kèm sẽ bị mất, và người dùng sẽ cần phải chọn lại nó một lần nữa. Vấn đề này rất dễ khắc phục bằng một plugin khác được gọi là cached_attachment_data:

models/image_uploader.rb

Bây giờ chỉ cần thêm một trường ẩn vào form của bạn.

views/photos/_form.html.erb

Chỉnh sửa một Tấm hình

Bây giờ hình ảnh đã có thể được tải lên, nhưng không có cách nào để chỉnh sửa chúng, vì vậy, hãy khắc phục điều đó ngay. Các hành động của controller tương ứng là không quan trọng lắm:

photos_controller.rb

Một phần _form tương tự sẽ được sử dụng:

views/photos/edit.html.erb

Tốt, nhưng chưa đủ: người dùng vẫn không thể xóa hình ảnh đã tải lên. Để cho phép điều này, chúng ta sẽ cần—đoán được không—một plugin khác:

models/image_uploader.rb

Nó sử dụng một thuộc tính ảo được gọi là :remove_image, vì vậy hãy cho phép nó bên trong controller:

photos_controller.rb

Bây giờ chỉ cần hiển thị một hộp checkbox để xoá một hình ảnh nếu một bản ghi có một tập tin đính kèm ở đó:

views/photos/_form.html.erb

Tạo một Thumbnail

Hiện tại, chúng ta hiển thị hình ảnh gốc, đây không phải là cách làm tốt nhất đối với việc xem trước: ảnh có thể lớn và chiếm quá nhiều bộ nhớ. Tất nhiên, bạn có thể chỉ cần sử dụng các thuộc tính widthheight của CSS, nhưng đó cũng là một ý tưởng không tốt. Bạn thấy đó, ngay cả khi hình ảnh được thiết lập cho nhỏ lại bằng các phong cách CSS, thì người dùng vẫn sẽ cần phải tải về tập tin ban đầu, có thể là khá lớn.

Do đó, tốt hơn là tạo một hình ảnh xem trước nhỏ ở phía máy chủ trong quá trình tải lên ban đầu. Điều này liên quan đến hai plugin và hai gem bổ sung. Trước nhất, thêm gem:

image_processing là một gem đặc biệt được tạo ra bởi tác giả của Shrine. Nó đưa ra một số phương thức trợ giúp cấp cao để thao tác lên các hình ảnh. Gem này dựa vào mini_magick, một wrapper của Ruby cho ImageMagick. Như bạn có thể đoán được, bạn sẽ cần ImageMagick trên hệ thống của bạn để chạy bản demo này.

Cài đặt các gem mới này:

Bây giờ hãy bao gồm các plugin cùng với các phụ thuộc của chúng:

models/image_uploader.rb

Processing là plugin để thật sự thao tác lên một hình ảnh (ví dụ như thu nhỏ, xoay, chuyển đổi sang định dạng khác, v.v.). Versions lần lượt cho phép chúng ta có một hình ảnh ở trong các biến thể khác nhau. Đối với demo này, có hai phiên bản sẽ được lưu trữ: "original" và "thumb" (thay đổi kích thước thành 300x300).

Dưới đây là code để xử lý một hình ảnh và lưu trữ hai phiên bản của nó:

models/image_uploader.rb

resize_to_limit! là một phương thức được cung cấp bởi gem image_processing. Nó chỉ đơn giản là thu nhỏ một hình ảnh xuống thành 300x300 nếu nó lớn hơn và không làm gì nếu nó nhỏ hơn. Hơn nữa, nó giữ nguyên tỷ lệ.

Bây giờ khi hiển thị hình ảnh, bạn chỉ cần cung cấp đối số hoặc :thumbnail hoặc :original cho phương thức image_url:

views/photos/_photo.html.erb

Điều tương tự có thể được thực hiện bên trong form:

views/photos/_form.html.erb

Để tự động xóa các tập tin đã được xử lý sau khi đã tải lên, bạn có thể thêm plugin được gọi là delete_raw:

models/image_uploader.rb

Metadata của Hình ảnh

Ngoài việc thật sự kết xuất một hình ảnh, bạn cũng có thể trích ra metadata của nó. Ví dụ, hãy hiển thị kích thước và loại MIME của của hình ảnh gốc:

views/photos/_photo.html.erb

Còn về kích thước của nó thì sao? Thật không may, mặc định chúng không được lưu trữ, nhưng điều này là có thể với một plugin có tên là store_dimensions.

Kích thước của Hình ảnh

Plugin store_dimensions dựa trên gem fastimage, vì vậy, hãy móc nối nó ngay bây giờ:

Đừng quên chạy nó:

Bây giờ chỉ cần bao gồm plugin:

models/image_uploader.rb

Và hiển thị các kích thước bằng cách sử dụng các phương thức widthheight:

views/photos/_photo.html.erb

Ngoài ra, có một phương thức dimensions trả về một mảng chứa chiều rộng và chiều cao (ví dụ [500, 750]).

Di chuyển lên Đám mây

Nhà phát triển thường chọn các dịch vụ đám mây để lưu trữ các tập tin được tải lên và Shrine khả năng đó. Trong phần này, tôi sẽ hướng dẫn cho bạn cách tải tập tin lên Amazon S3.

Bước đầu tiên, hãy bao gồm thêm hai gem nữa vào Gemfile:

aws-sdk được yêu cầu để làm việc với SDK của S3, trong khi dotenv-rails sẽ được sử dụng để quản lý các biến môi trường trong quá trình phát triển.

Trước khi tiến hành, bạn nên lấy một cặp khóa để truy cập S3 thông qua API. Để có được nó, hãy đăng nhập (hoặc đăng ký) vào Amazon Web Services Console và chuyển đến Security Credentials > Users. Tạo một người dùng với các quyền để thao tác lên các tập tin trên S3. Dưới đây là chính sách đơn giản có đầy đủ quyền truy cập vào S3:

Tải xuống cặp khóa của người dùng đã được tạo. Ngoài ra, bạn có thể sử dụng các khoá truy cập root (gốc), nhưng tôi thành thật không khuyến khích bạn làm điều đó vì nó rất không an toàn.

Tiếp theo, tạo một bucket trong S3 để lưu trữ các tập tin của bạn và thêm một tập tin vào gốc của dự án để lưu trữ cấu hình của bạn:

.env

Đừa bao giờ để lộ tập tin này ra ngoài và đảm bảo bạn loại trừ nó khỏi Git:

.gitignore

Bây giờ, hãy sửa đổi cấu hình toàn cục của Shrine và đưa ra một bộ lưu trữ mới:

config/initializers/shrine.rb

Xong rồi! Không cần phải thay đổi gì nữa cho các phần khác của ứng dụng và bạn có thể kiểm tra bộ lưu trữ mới ngay lập tức. Nếu bạn đang gặp lỗi từ S3 liên quan đến các khoá không chính xác, thì hãy chắc chắn rằng bạn đã sao chép các khoá và khoá bí mật một cách chính xác, không có dấu cách và ký hiệu đặc biệt bị mất.

Tóm tắt

Chúng ta đã đến phần cuối cùng của bài viết này. Hy vọng rằng, lúc này bạn cảm thấy tự tin hơn khi sử dụng Shrine và háo hức sử dụng nó trong một trong những dự án của bạn. Chúng ta đã thảo luận nhiều tính năng của gem này, nhưng có nhiều hơn thế, như khả năng lưu trữ ngữ cảnh bổ sung cùng với các tập tin và cơ chế tải lên trực tiếp.

Do đó, hãy tham khảo tài liệu hướng dẫn của Shrinetrang web chính thức của nó, nó mô tả một cách kỹ lưỡng tất cả các plugin sẵn có. Nếu bạn còn có những câu hỏi về gem này, thì đừng ngần ngại hỏi chúng. Cảm ơn bạn đã theo dõi, và hẹn sớm gặp lại bạn!

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.