使用activestorage的直接上传上传到S3时如何指定前缀?
How to specify a prefix when uploading to S3 using activestorage's direct upload?
使用标准 S3 配置:
AWS_ACCESS_KEY_ID: [AWS ID]
AWS_BUCKET: [bucket name]
AWS_REGION: [region]
AWS_SECRET_ACCESS_KEY: [secret]
我可以使用此 Rails 5.2 代码(仅显示相关代码)将文件上传到 S3(使用直接上传):
form.file_field :my_asset, direct_upload: true
提交表单后,这将有效地将我的资产放入我的 S3 存储桶的根目录中。
如何指定前缀(例如 "development/",以便我可以模拟 S3 上的文件夹)?
抱歉,目前无法进行。我建议创建一个供 Active Storage 专用的存储桶。
我目前在 S3 上的解决方法(至少在 ActiveStorage 引入为 has_one_attached
和 has_many_attached
宏传递路径的选项之前)是实现 move_to method。
所以我让 ActiveStorage 像现在一样将图像保存到 S3(在存储桶的顶部),然后将文件移动到文件夹结构中。
move_to
方法基本上是将文件复制到您传递的文件夹结构中,然后删除放在存储桶根目录下的文件。这样您的文件就可以到达您想要的位置。
因此,例如,如果我们要存储驱动程序详细信息:name
和 drivers_license
,请按照您已经在做的方式保存它们,以便它位于存储桶的顶部。
然后实现以下(我把我的放在一个助手中):
module DriversHelper
def restructure_attachment(driver_object, new_structure)
old_key = driver_object.image.key
begin
# Passing S3 Configs
config = YAML.load_file(Rails.root.join('config', 'storage.yml'))
s3 = Aws::S3::Resource.new(region: config['amazon']['region'],
credentials: Aws::Credentials.new(config['amazon']['access_key_id'], config['amazon']['secret_access_key']))
# Fetching the licence's Aws::S3::Object
old_obj = s3.bucket(config['amazon']['bucket']).object(old_key)
# Moving the license into the new folder structure
old_obj.move_to(bucket: config['amazon']['bucket'], key: "#{new_structure}")
update_blob_key(driver_object, new_structure)
rescue => ex
driver_helper_logger.error("Error restructuring license belonging to driver with id #{driver_object.id}: #{ex.full_message}")
end
end
private
# The new structure becomes the new ActiveStorage Blob key
def update_blob_key(driver_object, new_key)
blob = driver_object.image_attachment.blob
begin
blob.key = new_key
blob.save!
rescue => ex
driver_helper_logger.error("Error reassigning the new key to the blob object of the driver with id #{driver_object.id}: #{ex.full_message}")
end
end
def driver_helper_logger
@driver_helper_logger ||= Logger.new("#{Rails.root}/log/driver_helper.log")
end
end
更新 blob 键很重要,这样对键的引用就不会 return 错误。
如果密钥未更新,则任何尝试引用该图像的函数都将在它以前的位置(在存储桶的顶部)而不是在它的新位置查找它。
文件一保存(即在创建操作中),我就从我的控制器中调用此函数,这样它看起来是无缝的,尽管实际上并非如此。
虽然这可能不是最好的方法,但它现在有效。
仅供参考:根据您给出的示例,new_structure
变量将为 new_structure = "development/#{driver_object.image.key}"
。
希望对您有所帮助! :)
索尼娅,谢谢你的回答。
我尝试了您的解决方案并且效果很好,但我遇到了覆盖附件的问题。我在做的时候经常遇到 IntegrityError。我认为,这个和 checksum 处理可能是 Rails 核心团队不想添加传递路径名功能的原因。这需要更改 upload 方法的整个逻辑。
ActiveStorage::Attached#create_from_blob 方法,也可以接受一个 ActiveStorage::Blob 对象。所以我尝试了一种不同的方法:
- 手动创建一个 Blob,使用代表所需文件结构和上传附件的键。
- 使用 ActiveStorage 方法附加创建的 Blob。
在我的使用中,解决方案是这样的:
def attach file # method for attaching in the model
blob_key = destination_pathname(file)
blob = ActiveStorage::Blob.find_by(key: blob_key.to_s)
unless blob
blob = ActiveStorage::Blob.new.tap do |blob|
blob.filename = blob_key.basename.to_s
blob.key = blob_key
blob.upload file
blob.save!
end
end
# Attach method from ActiveStorage
self.file.attach blob
end
感谢将完整路径名传递给 Blob 的密钥,我在服务器上收到了所需的文件结构。
以上解决方案还是会报IntegrityError,需要使用File.open(file)。感谢 Though 的想法。
class History < ApplicationRecord
has_one_attached :gs_history_file
def attach(file) # method for attaching in the model
blob_key = destination_pathname(file)
blob = ActiveStorage::Blob.find_by(key: blob_key.to_s)
unless blob
blob = ActiveStorage::Blob.new.tap do |blob|
blob.filename = blob_key.to_s
blob.key = blob_key
#blob.byte_size = 123123
#blob.checksum = Time.new.strftime("%Y%m%d-") + Faker::Alphanumeric.alpha(6)
blob.upload File.open(file)
blob.save!
end
end
# Attach method from ActiveStorage
self.gs_history_file.attach blob
end
def destination_pathname(file)
"testing/filename-#{Time.now}.xlsx"
end
end
2022更新:从Rails 6.1开始(勾选this commit),实际支持:
user.avatar.attach(key: "avatars/#{user.id}.jpg", io: io, content_type: "image/jpeg", filename: "avatar.jpg")
2022 年的解决方案是什么?我想将我的文件存储在
my-bucket/some-folder/myfile.somefile
用 Active Storage 很好地做到这一点似乎仍然是不可能的。
使用标准 S3 配置:
AWS_ACCESS_KEY_ID: [AWS ID]
AWS_BUCKET: [bucket name]
AWS_REGION: [region]
AWS_SECRET_ACCESS_KEY: [secret]
我可以使用此 Rails 5.2 代码(仅显示相关代码)将文件上传到 S3(使用直接上传):
form.file_field :my_asset, direct_upload: true
提交表单后,这将有效地将我的资产放入我的 S3 存储桶的根目录中。
如何指定前缀(例如 "development/",以便我可以模拟 S3 上的文件夹)?
抱歉,目前无法进行。我建议创建一个供 Active Storage 专用的存储桶。
我目前在 S3 上的解决方法(至少在 ActiveStorage 引入为 has_one_attached
和 has_many_attached
宏传递路径的选项之前)是实现 move_to method。
所以我让 ActiveStorage 像现在一样将图像保存到 S3(在存储桶的顶部),然后将文件移动到文件夹结构中。
move_to
方法基本上是将文件复制到您传递的文件夹结构中,然后删除放在存储桶根目录下的文件。这样您的文件就可以到达您想要的位置。
因此,例如,如果我们要存储驱动程序详细信息:name
和 drivers_license
,请按照您已经在做的方式保存它们,以便它位于存储桶的顶部。
然后实现以下(我把我的放在一个助手中):
module DriversHelper
def restructure_attachment(driver_object, new_structure)
old_key = driver_object.image.key
begin
# Passing S3 Configs
config = YAML.load_file(Rails.root.join('config', 'storage.yml'))
s3 = Aws::S3::Resource.new(region: config['amazon']['region'],
credentials: Aws::Credentials.new(config['amazon']['access_key_id'], config['amazon']['secret_access_key']))
# Fetching the licence's Aws::S3::Object
old_obj = s3.bucket(config['amazon']['bucket']).object(old_key)
# Moving the license into the new folder structure
old_obj.move_to(bucket: config['amazon']['bucket'], key: "#{new_structure}")
update_blob_key(driver_object, new_structure)
rescue => ex
driver_helper_logger.error("Error restructuring license belonging to driver with id #{driver_object.id}: #{ex.full_message}")
end
end
private
# The new structure becomes the new ActiveStorage Blob key
def update_blob_key(driver_object, new_key)
blob = driver_object.image_attachment.blob
begin
blob.key = new_key
blob.save!
rescue => ex
driver_helper_logger.error("Error reassigning the new key to the blob object of the driver with id #{driver_object.id}: #{ex.full_message}")
end
end
def driver_helper_logger
@driver_helper_logger ||= Logger.new("#{Rails.root}/log/driver_helper.log")
end
end
更新 blob 键很重要,这样对键的引用就不会 return 错误。
如果密钥未更新,则任何尝试引用该图像的函数都将在它以前的位置(在存储桶的顶部)而不是在它的新位置查找它。
文件一保存(即在创建操作中),我就从我的控制器中调用此函数,这样它看起来是无缝的,尽管实际上并非如此。
虽然这可能不是最好的方法,但它现在有效。
仅供参考:根据您给出的示例,new_structure
变量将为 new_structure = "development/#{driver_object.image.key}"
。
希望对您有所帮助! :)
索尼娅,谢谢你的回答。
我尝试了您的解决方案并且效果很好,但我遇到了覆盖附件的问题。我在做的时候经常遇到 IntegrityError。我认为,这个和 checksum 处理可能是 Rails 核心团队不想添加传递路径名功能的原因。这需要更改 upload 方法的整个逻辑。
ActiveStorage::Attached#create_from_blob 方法,也可以接受一个 ActiveStorage::Blob 对象。所以我尝试了一种不同的方法:
- 手动创建一个 Blob,使用代表所需文件结构和上传附件的键。
- 使用 ActiveStorage 方法附加创建的 Blob。
在我的使用中,解决方案是这样的:
def attach file # method for attaching in the model
blob_key = destination_pathname(file)
blob = ActiveStorage::Blob.find_by(key: blob_key.to_s)
unless blob
blob = ActiveStorage::Blob.new.tap do |blob|
blob.filename = blob_key.basename.to_s
blob.key = blob_key
blob.upload file
blob.save!
end
end
# Attach method from ActiveStorage
self.file.attach blob
end
感谢将完整路径名传递给 Blob 的密钥,我在服务器上收到了所需的文件结构。
以上解决方案还是会报IntegrityError,需要使用File.open(file)。感谢 Though 的想法。
class History < ApplicationRecord
has_one_attached :gs_history_file
def attach(file) # method for attaching in the model
blob_key = destination_pathname(file)
blob = ActiveStorage::Blob.find_by(key: blob_key.to_s)
unless blob
blob = ActiveStorage::Blob.new.tap do |blob|
blob.filename = blob_key.to_s
blob.key = blob_key
#blob.byte_size = 123123
#blob.checksum = Time.new.strftime("%Y%m%d-") + Faker::Alphanumeric.alpha(6)
blob.upload File.open(file)
blob.save!
end
end
# Attach method from ActiveStorage
self.gs_history_file.attach blob
end
def destination_pathname(file)
"testing/filename-#{Time.now}.xlsx"
end
end
2022更新:从Rails 6.1开始(勾选this commit),实际支持:
user.avatar.attach(key: "avatars/#{user.id}.jpg", io: io, content_type: "image/jpeg", filename: "avatar.jpg")
2022 年的解决方案是什么?我想将我的文件存储在
my-bucket/some-folder/myfile.somefile
用 Active Storage 很好地做到这一点似乎仍然是不可能的。