使用activestorage直接上传时如何在上传到S3时指定前缀?

使用标准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的存储桶。

我当前的解决方法(至少在ActiveStorage引入了为has_one_attachedhas_many_attached宏传递路径的选项之前)是实现move_to方法 。

所以我让ActiveStorage像往常一样将图像保存到S3(在存储桶的顶部),然后将文件移动到文件夹结构中。

move_to方法基本上将文件复制到您传递的文件夹结构中,然后删除放在存储桶根目录下的文件。 这样您的文件就会在您想要的位置结束。

因此,例如,如果我们存储驱动程序详细信息: namedrivers_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密钥非常重要,这样对密钥的引用不会返回错误。

如果密钥未更新,则任何尝试引用图像的函数都会在其前一个位置(位于存储桶的顶部)而不是在其新位置中查找它。

一旦文件被保存(即在创建操作中),我就会从我的控制器调用此函数,这样即使它不是,它也看起来是无缝的。

虽然这可能不是最好的方法,但它现在有效。

仅供参考:根据您提供的示例, new_structure变量将为new_structure = "development/#{driver_object.image.key}"

我希望这有帮助! 🙂

感谢Sonia,谢谢你的回答。

我尝试了你的解决方案并且效果很好,但是我遇到了覆盖附件的问题。 我经常在做这件事时遇到IntegrityError 。 我认为,这和校验和处理可能是Rails核心团队不想添加传递路径名function的原因。 它需要更改上传方法的整个逻辑。

ActiveStorage :: Attached#create_from_blob方法,也可以接受ActiveStorage :: Blob对象。 所以我尝试了一种不同的方法:

  1. 使用表示所需文件结构和上载附件的键手动创建Blob
  2. 使用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的密钥,我在服务器上收到了所需的文件结构。