ActiveStorageSaas
ActiveStorageSaas 是一个 Rails gem,为 ActiveStorage 提供多租户存储服务支持。它允许每个租户配置自己的存储服务,并支持动态加载不同的存储服务。
功能特性
- 🏢 多租户支持:每个租户可以配置独立的存储服务
- 🔄 动态服务加载:根据 blob 的 service_name 动态解析并加载对应的存储服务
- 🔌 灵活配置:支持通过数据库配置存储服务选项
- 📦 完全兼容:与 Rails ActiveStorage API 完全兼容,无需修改现有代码
- 🚀 直接上传支持:支持 ActiveStorage 的直接上传功能
安装
在 Gemfile 中添加:
gem 'activestorage_saas', '~> 7.2.3'
然后运行:
bundle install
运行安装生成器:
rails generate active_storage_saas:install
这将创建必要的迁移文件。运行迁移:
rails db:migrate
配置
1. 数据库迁移
安装生成器会创建一个 storage_service_configurations 表,用于存储每个租户的存储服务配置。
2. 存储配置 (config/storage.yml)
在 config/storage.yml 中配置一个 saas 服务作为包装器:
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)
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
使用方法
创建存储服务配置
# 为租户创建存储服务配置
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
使用存储服务
# 创建 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 服务实例
在模型中关联文件
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 是一个包装器服务,它:
- 接收一个
blob对象 - 根据
blob.service_name动态解析对应的存储服务 - 将所有方法调用委托给实际的存储服务
服务解析流程
- 当访问
blob.service时,会调用ActiveStorageSaas.service_resolver - 解析器根据
service_name查找对应的StorageServiceConfiguration - 配置对象通过
to_service方法创建实际的存储服务实例 - 如果解析失败,回退到默认服务
服务名称格式
动态服务的名称格式为:ClassName:ID,例如:
StorageServiceConfiguration:1StorageServiceConfiguration:42
这种格式允许从服务名称中提取配置记录的 ID。
高级用法
自定义服务解析器
config.active_storage_saas.service_resolver = ->(blob) {
tenant = blob.record&.tenant
tenant&.storage_config&.to_service
}
多租户集成示例
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
开发
运行测试:
bundle exec rspec
运行控制台:
bin/console
贡献
欢迎提交 Issue 和 Pull Request!
许可证
MIT License