Search code examples
ruby-on-railsrubyruby-on-rails-6rails-activestorage

active storage - multiple services - direct upload not working as expected


I have two services defined in the storage.yml

amazon:
  service: S3
  bucket: bucket1
  region: eu-central-1
  access_key_id: 321
  secret_access_key: 321

unsafe_files:
  service: S3
  bucket: unsafe-files
  region: eu-central-1
  access_key_id: 123
  secret_access_key: 123

I use the amazon service for some files and I wanted to use the unsafe_files service for other files so that I can put the into another s3 bucket.

The models that use the unsafe_files service look like that (very simple):

class Customer < ActiveRecord::Base
  belongs_to :customer_image
end
class CustomerImage < Image
  has_one_attached :file, service: Rails.configuration.settings[:unsafe_files_service]
end

Rails.configuration.settings[:unsafe_files_service] is just unsafe_files

in rails application.rb I'm setting

config.active_storage.service = :amazon so that by default it uses the amazon service defined in storage.yml

Now I want to direct upload the files using form_with, it's also quite simple:

<%= form_with model: [@customer, CustomerImage.new] do |form| %>
  <%=
    form.file_field :file,
                    accept: 'image/jpeg',
                    direct_upload: true,
                    multipart: true
  %>
  <%= form.submit "submit" %>
<% end %>

now I know (or I think I know) how direct upload works under the hood in rails when using active_storage. Some javascript listens for the on form submit event and first takes what's in the file_field and sends it to the DirectUploadsController which checks the image, generates the direct upload url and then we directly upload the file to the specified service. It's all here: https://github.com/rails/rails/blob/6ecf1065da57360bdc9f1d85e2c2d9314dcb79e0/activestorage/app/controllers/active_storage/direct_uploads_controller.rb#L14

The service responds with the file key or id. Afterwards the form submission continues and we save the id that we have received and that's how the relationship between the file.

But when we hit the DirectUploadsController and we create the blob it gets the default service_name (see: https://github.com/rails/rails/blob/6ecf1065da57360bdc9f1d85e2c2d9314dcb79e0/activestorage/app/models/active_storage/blob.rb#L115)

So long story short, when using direct upload I can't choose a service I have to rely on rails default service.

Is there a workaround for that? Or maybe I have missed something?


Solution

  • If you want to call create_before_direct_upload! yourself (so that you can specify the service_name) then I think you must override DirectUploadsController somehow. You could write your own controller, or (advanced) try to re-open and patch DirectUploadsController. My app does the former.

    Update from the OP:

    I ended up re-opening DirectUploadsController (and also adding an option to the form file_field to pass a custom direct_upload_url It works nicely although I don't like that I had to mingle with internals soo much. I've checked active_storage gem and it seems that one would have to just add this feature cause with current implementation there is no workaround. – beniutek