Handling file upload using Ruby on Rails 5 API
Bài đăng này đã không được cập nhật trong 7 năm
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
All rights reserved