Handling file upload using Ruby on Rails 5 API
Việc gửi các dữ liệu JSON cơ bản được tạo ra từ các strings đến một API (giao diện lập trình ứng dụng), trong hầu hết các trường hợp đều khá đơn giản và dễ hiểu.Gần đây mình có làm một vài thứ nho nhỏ liên quan đến upload file và mình đọc được một bài viết khá hữu ích muốn chia sẻ cùng mọi ...
Việc gửi các dữ liệu JSON cơ bản được tạo ra từ các strings đến một API (giao diện lập trình ứng dụng), trong hầu hết các trường hợp đều khá đơn giản và dễ hiểu.Gần đây mình có làm một vài thứ nho nhỏ liên quan đến upload file và mình đọc được một bài viết khá hữu ích muốn chia sẻ cùng mọi người. Làm thế nào để gửi các tập tin bao gồm nhiều dòng dữ liệu nhị phân trong các định dạng khác nhau? Dữ liệu đó yêu cầu một cách tiếp cận hơi khác để gửi files tới API. Trong hướng dẫn này chúng ta sẽ khảo sát cách tiếp cận chính để xử lý tải lên tệp tin, dữ liệu dạng nhiều phần và mã hoá base64, thông qua ứng dụng API Rails 5 sử dụng gem carrierwave. Adding Carrierwave vào trong gem file của bạn:
#Gemfile.rb gem "carrierwave"
Run install:
bundle install
Uploading a single file
Để tạo một uploader cho các picture của Item model, hãy chạy lệnh sau:
rails g uploader Picture rails g migration add_picture_to_items picture:string
Đầu tiên chúng ta sẽ tạo ra thư mục mới trong application - app/uploaders, đây là nơi mà tất cả các dữ liệu tải lên được lưu trữ tại đây Tiếp theo generator sẽ add một picture column như là string vào Item table nơi sẽ lưu trữ liên kết của file(ảnh, tài liệu...) Tất nhiên bạn cũng đừng quên migrate lại database nhé:
rails db:migrate
Trong generated uploader bạn sẽ nhìn thấy rất nhiều lựa chọn để config.Bây giờ chúng ta cần validate định dạng của file tải lên.Vì uploader đang muốn up ảnh lên, vì vậy chúng ta có thể validate như sau:
#app/uploaders/picture_uploader def extension_white_list %w(jpg jpeg gif png) end
Bây giờ ta sẽ mount uploader vào item model:
class Item < ApplicationRecord mount_uploader :picture, PictureUploader end
Khi một new model instance được tạo ra, uploader sẽ tự động liên kết picture với nó.Picture bao gồm url và ảnh, chúng ta sẽ gọi nó thông qua Item.picture.url Tiếp theo, điền :picture vào params:
#app/controllers/items_controllerr.rb def item_params params.require(:item).permit(:name, :description :picture) # Add :picture as a permitted paramter end
Cuối cùng chúng ta thêm :picture vào Jbuilder view, vì thế khi GET một item, API sẽ trả về picture của nó:
#app/views/items/show.json.jbuilder json.extract! @item, :id, :name, :description, :picture, :created_at, :updated_at
Uploading multiple files
Chúng ta sẽ add pdf documents vào item.Nó sẽ yêu cầu một uploader khác bằng với một model name document thuộc về item đó.Trước tiên chúng ta cần 2 câu lệnh generate sau:
rails g model document item:references document:string rails g uploader Document
Về generate đầu tiên, nó sẽ tạo một document model với liên kết tới Item và tới 1 file column sau đó liên kết với CarrierWave file.Thứ 2 generate sẽ tạo uploader trong thư mục app/uploaders.Tạo mới document model ở trong database schema:
rails db:migrate
Mount uploader vào document model:
#app/model/document.rb class Document < ApplicationRecord belongs_to :item mount_uploader :document, DocumentUploader end
Vì documents chỉ là PDF thế nên phần validate ta sẽ làm như sau:
#app/uploaders/document_uploader.rb def extension_white_list %w(pdf) end
Trong Item model, thêm vào các dòng sau:
#app/model/item.rb class Item < ApplicationRecord mount_uploader :picture, PictureUploader has_many :documents attr_accessor :document_data end
has_many :documents sẽ tạo một quan hệ kiểu one-to-many giữa item và documents, attr_accessor :document_data sẽ gửi một extra attributes tới controller.Attribute sẽ chứa một mảng với data về tất cả PDF document. Như vậy chúng ta đã xong với model!Hãy cùng nhau xử lí controller nào: 1.Thêm document_data array như một permitted parameter.Array phải luôn luôn là last parameter:
#app/controllers/item_controller.rb def item_params params.require(:item).permit(:name, :description, :picture, :document_data => []) #add document_data as a permitted parameter end
2.Tạo một vòng lặp sẽ tạo documents sau khi item được lưu:
#app/controllers/item_controller.rb def create @item = Item.new(item_params) if @item.save #iterate through each of the files params[:item][:document_data].each do |file| @item.documents.create!(:document => file) #create a document associated with the item that has just been created end render :show, status: :created, location: @item else render json: @item.errors, status: :unprocessable_entity end end
Cuối cùng chúng ta update Jbuilder view, vì thế documents trả về với item:
#app/views/items/show.json.jbuilder json.extract! @item, :id, :name, :description, :picture, :documents, :created_at, :updated_at
base 64 upload
Uploading base64-encoded file là việc rất đơn giản với Carrierwave:
# Gemfile.rb gem 'carrierwave-base64'
Cài đặt:
bundle install
Trong model chúng ta thay thế mount_uploader bằng mount_base64_uploader
#app/models/item.rb mount_base64_uploader :picture, PictureUploader
#app/models/document.rb mount_base64_uploader :document, DocumentUploader
Tuyệt vời, ứng dụng của bạn bây giờ có thể cho phép sử dụng base64-encoded file, thật đơn giản phải không nào.
Format of the request
Using multipart form data
Để POST-ing multipart form data, cURL là đủ.Điều thứ 2 là cần nhớ là khi sending files, chắc chắn rằng: đã khai báo type của chúng: type=application/pdf, nếu không cURL sends files như là binary type: application/octet-stream, nó sẽ làm hỏng mất một số validation.
curl -F "item[document_data][]=@E:ile2.pdf;type=application/pdf" -F "item[document_data][]=@E:ile1.pdf;type=application/pdf" -F "item[picture]=@E:photo.jpg" -F "item[name]=item" -F "item[description]=desc" localhost:3000/items
Using base64 strings in JSON
Nếu bạn muốn send base64 data, luôn nhớ rằng string phải dài và cURL phải đơn giản để sử dụng.Khuyến khích các bạn dùng Postman.Lưu ý cần thay thế picture bằng image_base nếu bạn sử dụng Paperclip.Đây là JSON format mà API được chấp nhận.
{ "item": { "name": "tem", "description": "desc", "picture": "data:image/png;base64, Rt2...", "document_data": [ "data:application/pdf;base64, fW3..." , "data:application/pdf;base64, pLf..."] } }
Ngoài ra chúng ta có thể sử dụng gem paperclip, tham khảo trong link: https://www.pluralsight.com/guides/ruby-ruby-on-rails/handling-file-upload-using-ruby-on-rails-5-api