Custom Direct Uploads Controller

Active Storage มาพร้อมกับฟีเจอร์ Direct Uploads ซึ่งทำให้ client สามารถอัพโหลดไฟล์ขึ้นไป cloud ซึ่งเจ้า Direct Uploads ก็จะผูกกับ service ที่กำหนดไว้

สำหรับ Web Application ที่ใช้งาน S3 ในการจัดเก็บไฟล์ และมีความต้องการในการแยก bucket เป็น 2 อัน ซึ่งอันแรกจะใช้งานแบบ private ส่วนอีกอันจะเป็นแบบ public จะพบปัญหาที่ว่า Direct Uploads นั้นจะถูกผูกอยู่กับ service ที่ได้กำหนดไว้ใน config เท่านั้น ดังสังเกตได้จาก code ด้านล่าง

# https://github.com/rails/rails/blob/main/activestorage/lib/active_storage/engine.rb
initializer "active_storage.services" do
  ActiveSupport.on_load(:active_storage_blob) do
    ...

    if config_choice = Rails.configuration.active_storage.service
      ActiveStorage::Blob.service = ActiveStorage::Blob.services.fetch(config_choice)
    end
  end
end

จากที่ได้เข้าไปศึกษาโค้ดของ Active Storage ก็พบว่า ActiveStorage::Blob นั้นสามารถรับ service_name เพื่อเลือก service ที่จะใช้งานตอนที่จะสร้าง Model ได้

# https://github.com/rails/rails/blob/main/activestorage/app/models/active_storage/blob.rb
def create_before_direct_upload!(key: nil, filename:, byte_size:, checksum:, content_type: nil, metadata: nil, service_name: nil, record: nil)
  create! key: key, filename: filename, byte_size: byte_size, checksum: checksum, content_type: content_type, metadata: metadata, service_name: service_name
end

ดังนั้นวิธีการง่ายๆ ที่ใช้ตอนนี้ก็คือการสร้าง Custom Controller ที่กำหนด service_name ที่จะใช้แยกออกมา และกำหนด route เพื่อใช้ Direct Uploads ไฟล์เข้าไปยัง public_bucket ใหม่ขึ้นมา

class CustomDirectUploadsController < ActiveStorage::DirectUploadsController

  def create
    blob = ActiveStorage::Blob.create_before_direct_upload!(**blob_args.merge(service_name: :public_bucket))
    render json: direct_upload_json(blob)
  end
end

และในฝั่งของ client ก็เพียงเปลี่ยน URL ให้ตรงกับ route ใหม่ เราก็สามารถที่จะอัพโหลดไฟล์แยกไปยังอีก bucket หนึ่งได้