07/09/2018, 08:36

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

0