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

时间:2018-01-22 20:30:29

标签: ruby-on-rails amazon-s3 rails-activestorage ruby-on-rails-5.2

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

如何指定前缀(例如"开发/",以便我可以模仿S3上的文件夹)?

4 个答案:

答案 0 :(得分:6)

对不起,目前还不行。我建议创建一个专门用于Active Storage的存储桶。

答案 1 :(得分:6)

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

所以我让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}"

我希望这有帮助! :)

答案 2 :(得分:3)

谢谢你,索尼亚(Sonia)。

我尝试了您的解决方案,效果很好,但是我遇到了覆盖附件的问题。在执行此操作时,我经常会遇到 IntegrityError 。我认为,这和 checksum 处理可能是Rails核心团队不想添加传递路径名功能的原因。这将需要更改上传方法的整个逻辑。

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 的密钥,我在服务器上收到了所需的文件结构。

答案 3 :(得分:0)

上述解决方案仍然会给出 IntegrityError ,需要使用File.open(file)。谢谢While的想法。

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