活动存储 - 多项服务 - 直接上传未按预期工作

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

我在 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

我对一些文件使用亚马逊服务,我想对其他文件使用 unsafe_files 服务,以便我可以将它们放入另一个 s3 存储桶。

使用unsafe_files服务的模型是这样的(很简单):

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] 就是 unsafe_files

在railsapplication.rb我正在设置

config.active_storage.service = :amazon 这样默认情况下它使用 storage.yml

中定义的 amazon 服务

现在想直接用form_with上传文件,也很简单:

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

现在我知道(或者我想我知道)在 rails 中使用 active_storage 时直接上传是如何工作的。 一些 javascript 监听表单提交事件并首先获取 file_field 中的内容并将其发送到 DirectUploadsController 检查图像,生成直接上传 url 然后我们直接将文件上传到指定的服务。都在这里:https://github.com/rails/rails/blob/6ecf1065da57360bdc9f1d85e2c2d9314dcb79e0/activestorage/app/controllers/active_storage/direct_uploads_controller.rb#L14

服务以文件密钥或 ID 响应。 之后表单提交继续,我们保存我们收到的 id,这就是文件之间的关系。

但是当我们点击 DirectUploadsController 并创建 blob 时,它会获得默认值 service_name(参见:https://github.com/rails/rails/blob/6ecf1065da57360bdc9f1d85e2c2d9314dcb79e0/activestorage/app/models/active_storage/blob.rb#L115

长话短说,当使用直接上传时,我无法选择服务,我必须依赖 rails 默认服务。

有解决办法吗?或者我错过了什么?

如果您想自己调用 create_before_direct_upload!(以便您可以指定 service_name),那么我认为您必须以某种方式覆盖 DirectUploadsController。您可以编写自己的控制器,或者(高级)尝试重新打开并修补 DirectUploadsController。我的应用程序执行前者。

来自 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

这是一个已知问题。此 PR 是针对该问题的,但由于另一个主题而被还原。

我解决此问题的方法是覆盖 DirectUploadsController 中的 blob_args 方法。

# app/overrides/direct_uploads_controller_override.rb
ActiveStorage::DirectUploadsController.class_eval do
  alias_method :old_blob_args, :blob_args
  def blob_args
    old_blob_args.merge(service_name: params[:service])
  end
end

加载覆盖:

# config/application.rb
# ...
overrides = "#{Rails.root}/app/overrides"
Rails.autoloaders.main.ignore(overrides)

config.to_prepare do
  Dir.glob("#{overrides}/**/*_override.rb").each do |override|
    load override
  end
end

不使用 direct_upload 选项,而是手动设置数据属性 data-direct-upload-url。例如:

<%= form.file_field :file, multipart: true, 
    "data-direct-upload-url" => rails_direct_uploads_url(service: "unsafe_files") %>