ActiveStorageSaas

ActiveStorageSaas 是一个 Rails gem,为 ActiveStorage 提供多租户存储服务支持。它允许每个租户配置自己的存储服务,并支持动态加载不同的存储服务。

功能特性

  • 🏢 多租户支持:每个租户可以配置独立的存储服务
  • 🔄 动态服务加载:根据 blob 的 service_name 动态解析并加载对应的存储服务
  • 🔌 灵活配置:支持通过数据库配置存储服务选项
  • 📦 完全兼容:与 Rails ActiveStorage API 完全兼容,无需修改现有代码
  • 🚀 直接上传支持:支持 ActiveStorage 的直接上传功能

安装

在 Gemfile 中添加:

ruby gem 'activestorage_saas', '~> 7.2.3'

然后运行:

bash bundle install

运行安装生成器:

bash rails generate active_storage_saas:install

这将创建必要的迁移文件。运行迁移:

bash rails db:migrate

配置

1. 数据库迁移

安装生成器会创建一个 storage_service_configurations 表,用于存储每个租户的存储服务配置。

2. 存储配置 (config/storage.yml)

config/storage.yml 中配置一个 saas 服务作为包装器:

```yaml saas: service: Saas default_service: local # 默认使用的存储服务

local: service: Disk root: <%= Rails.root.join(“storage”) %>

amazon: service: S3 access_key_id: <%= ENV[‘AWS_ACCESS_KEY_ID’] %> secret_access_key: <%= ENV[‘AWS_SECRET_ACCESS_KEY’] %> region: us-east-1 bucket: my-bucket ```

3. 应用配置 (config/application.rb 或 config/environments/*.rb)

```ruby Rails.application.configure do # 可选:自定义服务解析器 # config.active_storage_saas.service_resolver = ->(blob) { # StorageServiceConfiguration.from_service_name(blob.service_name)&.to_service # }

# 可选:自定义服务名称转换器(用于直接上传) # config.active_storage_saas.service_name_converter = ->(controller) { # controller.send(:current_tenant).storage_service&.to_service_name # }

# 可选:自定义服务名称验证器 # config.active_storage_saas.service_name_validator = ->(service_name) { # StorageServiceConfiguration.valid_service_name?(service_name) # }

# 可选:直接上传时额外的 blob 参数 # config.active_storage_saas.direct_upload_extra_blob_args = ->(controller) { # { tenant: controller.send(:current_tenant) } # } end ```

使用方法

创建存储服务配置

```ruby # 为租户创建存储服务配置 config = StorageServiceConfiguration.create!( service_name: ‘amazon’, # 基础服务名称(在 storage.yml 中定义) service_options: { bucket: ‘tenant-specific-bucket’, region: ‘us-west-2’ } )

获取服务名称(格式:StorageServiceConfiguration:123)

service_name = config.to_service_name ```

使用存储服务

```ruby # 创建 blob 时指定服务名称 blob = ActiveStorage::Blob.create!( filename: ‘example.jpg’, byte_size: 1024, checksum: ‘abc123’, content_type: ‘image/jpeg’, service_name: config.to_service_name # 使用动态服务名称 )

blob.service 会自动解析为对应的存储服务实例

blob.service # => 返回配置的 S3 服务实例 ```

在模型中关联文件

```ruby class User < ApplicationRecord has_one_attached :avatar end

上传文件时指定服务

user.avatar.attach( io: file, filename: ‘avatar.jpg’, service_name: current_tenant.storage_config.to_service_name ) ```

工作原理

SaasService

SaasService 是一个包装器服务,它:

  1. 接收一个 blob 对象
  2. 根据 blob.service_name 动态解析对应的存储服务
  3. 将所有方法调用委托给实际的存储服务

服务解析流程

  1. 当访问 blob.service 时,会调用 ActiveStorageSaas.service_resolver
  2. 解析器根据 service_name 查找对应的 StorageServiceConfiguration
  3. 配置对象通过 to_service 方法创建实际的存储服务实例
  4. 如果解析失败,回退到默认服务

服务名称格式

动态服务的名称格式为:ClassName:ID,例如: - StorageServiceConfiguration:1 - StorageServiceConfiguration:42

这种格式允许从服务名称中提取配置记录的 ID。

高级用法

自定义服务解析器

ruby config.active_storage_saas.service_resolver = ->(blob) { tenant = blob.record&.tenant tenant&.storage_config&.to_service }

多租户集成示例

```ruby class Tenant < ApplicationRecord has_one :storage_service_configuration, class_name: ‘StorageServiceConfiguration’ end

class ApplicationController < ActionController::Base def current_tenant @current_tenant ||= Tenant.find(session[:tenant_id]) end end

在配置中

config.active_storage_saas.service_name_converter = ->(controller) { controller.current_tenant.storage_service_configuration.to_service_name } ```

兼容性

  • Rails 版本:7.2.3
  • Ruby 版本:>= 2.6.0
  • ActiveStorage 版本:7.2.3

开发

运行测试:

bash bundle exec rspec

运行控制台:

bash bin/console

贡献

欢迎提交 Issue 和 Pull Request!

许可证

MIT License