Ruby on rails 在运行时更改ActiveStorage DirectDisk服务配置

Ruby on rails 在运行时更改ActiveStorage DirectDisk服务配置,ruby-on-rails,rails-activestorage,Ruby On Rails,Rails Activestorage,我使用的是Rails 5.2和ActiveStorage 5.2.3以及 为了让用户上传能够很好地分组到目录中,并且能够随心所欲地使用CDN(例如CloudFlare或CloudFront或任何其他),我正在尝试在ApplicationController中设置一个方法来设置上传的(本地)路径,例如: class ApplicationController < ActionController::Base before_action :set_uploads_path # ...

我使用的是Rails 5.2和ActiveStorage 5.2.3以及

为了让用户上传能够很好地分组到目录中,并且能够随心所欲地使用CDN(例如CloudFlare或CloudFront或任何其他),我正在尝试在ApplicationController中设置一个方法来设置上传的(本地)路径,例如:

class ApplicationController < ActionController::Base
  before_action :set_uploads_path

  # ...

private
  def set_upload_paths
     # this doesn't work
     ActiveStorage::Service::DirectDiskService.set_root p: "users/#{current_user.username}"
     # ... expecting the new root to become "public/users/yaddayadda"
  end
end
它确实允许我在服务的新实例上设置根,但不允许在Rails应用程序正在使用的实例上设置根


我做错了什么?或者我该如何做到这一点呢?

这里有一个肮脏的黑客程序,可以帮助您将本地(磁盘)上传内容分组到public/websites/domain\u name/uploads下

步骤1)从此处安装ActiveStorage DirectDisk服务:

步骤2)应用程序/模型/活动存储/当前.rb中

步骤4)在config/initializers/active_storage.rb中

步骤5)在app/controllers/application\u controller.rb中

步骤6)在config/storage.yml中

缺点

虽然ActiveRecord在技术上“有效”,但它缺少一些非常重要的特性,这些特性使它无法为大多数人所用,因此最终开发人员将倾听并调整它;此时,您可能需要重新访问此代码和所有上载内容

该服务试图“猜测”一个blob所附加的类名,因为AS没有传递该类名,所以它会对您的数据库运行额外的2-3个查询。如果这让你感到困扰,只需删除该部分,并将其全部放在“网站/域名/上传/全部”下即可/

在某些情况下(例如变体,或带有action_text列的新记录),它无法确定附件记录及其类名,因此将在websites/domain/uploads/all/…下上载

module SetDirectDiskServiceRoot
    def set_root(p:)
        @root = Rails.root.join("public", p)
        @public_root = p
        @public_root.prepend('/') unless @public_root.starts_with?('/')
    end
end

ActiveStorage::Service::DirectDiskService.module_eval { attr_writer :root }
ActiveStorage::Service::DirectDiskService.class_eval { include SetDirectDiskServiceRoot }
class ActiveStorage::Current < ActiveSupport::CurrentAttributes #:nodoc:
  attribute :host
  attribute :domain_name
end
module SetCurrentDomainName
    def set_domain_name(d)
        self.domain_name = d
    end
end

ActiveStorage::Current.class_eval { include SetCurrentDomainName }

module SetDirectDiskServiceRoot
    def initialize(p:, public: false, **options)
        @root = Rails.root.join("public", p)
        @public_root = p
        @public_root.prepend('/') unless @public_root.starts_with?('/')
        puts options
    end

    def current_domain_name
        ActiveStorage::Current.domain_name
    end

    def folder_for(key)
        # original: File.join root, folder_for(key), key
        p = [ current_domain_name, "uploads", "all", key ]
        blob = ActiveStorage::Blob.find_by(key: key)
        if blob
            att = blob.attachments.first
            if att
                rec = att.record
                if rec
                    p = [ current_domain_name, "uploads", rec.class.name.split("::").last.downcase, rec.id.to_s, att.name, key ]
                end
            end
        end
        return File.join p
    end
end

ActiveStorage::Service::DirectDiskService.module_eval { attr_writer :root }
ActiveStorage::Service::DirectDiskService.class_eval { include SetDirectDiskServiceRoot }
require Rails.root.join("lib", "set_direct_disk_service_path.rb")
before_action :set_active_storage_domain_name

# ...
def set_active_storage_domain_name
    ActiveStorage::Current.domain_name = current_website.domain_name # or request.host
end
development:
  service: DirectDisk
  root: 'websites_development'

production:
  service: DirectDisk
  root: 'websites'