如何使用S3

时间:2018-04-12 08:33:25

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

rails version 5.2

我有一个场景,我需要使用Amazon s3访问Rails Active Storage的公共URL来制作带Sidekiq后台作业的zip文件。

我很难获得实际的文件网址。我试过了

rails_blob_url

但它让我跟随

http://localhost:3000/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBZUk9IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--9598613be650942d1ee4382a44dad679a80d2d3b/sample.pdf

如何通过sidekiq访问真实文件网址?

storage.yml

 test:
  service: Disk
  root: <%= Rails.root.join("tmp/storage") %>

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

development:
  service: S3
  access_key_id: 'xxxxx'
  secret_access_key: 'xxxxx'
  region: 'xxxxx'
  bucket: 'xxxxx'

development.rb

  config.active_storage.service = :development

我可以在网络界面上访问这些,但不能在sidekiq

中访问

5 个答案:

答案 0 :(得分:23)

使用ActiveStorage::Blob#service_url。例如,假设Post模型附加了一个header_image

@post.header_image.service_url

答案 1 :(得分:3)

我的用例是将图像上传到S3,该桶可以对存储桶中的所有图像进行公共访问,因此无论请求的来源或URL到期如何,作业都可以在以后拾取它们。这就是我做的。 (Rails 5.2.2)

首先,将新S3降压的默认设置是将所有内容都设为私有,因此要失败就必须进行2步操作。

  1. 添加通配符存储区策略。在AWS S3中>>您的存储桶>>权限>>存储桶策略
{
    "Version": "2008-10-17",
    "Statement": [
        {
            "Sid": "AllowPublicRead",
            "Effect": "Allow",
            "Principal": "*",
            "Action": "s3:GetObject",
            "Resource": "arn:aws:s3:::your-bucket-name/*"
        }
    ]
}
  1. 在您的存储桶>>权限>>公共访问设置中,确保将Block public and cross-account access if bucket has public policies设置为false

现在,您只需使用URL中的blob.key就可以访问S3存储桶中的任何内容。不再需要过期的令牌。

第二,要生成该URL,您可以使用@Christian_Butzke的解决方案:@post.header_image.service.send(:object_for, @post.header_image.key).public_url

但是,请知道object_for是service上的私有方法,如果使用public_send进行调用会给您带来错误。因此,另一种替代方法是使用每个@George_Claghorn的service_url并仅使用url&.split("?")&.first删除任何参数。如前所述,在本地主机中这可能会失败,并且主机丢失错误。

这是我的解决方案,或者是一个可上传的“徽标”,该徽标存储在S3上并默认为公开:

#/models/company.rb
has_one_attached :logo
def public_logo_url
    if self.logo&.attachment
        if Rails.env.development?
            self.logo_url = Rails.application.routes.url_helpers.rails_blob_url(self.logo, only_path: true)
        else
            self.logo_url = self.logo&.service_url&.split("?")&.first
        end
    end
    #set a default lazily
    self.logo_url ||= ActionController::Base.helpers.asset_path("default_company_icon.png")
end

享受^ _ ^

答案 2 :(得分:2)

如果需要公开所有文件,则必须公开上传:

在config / storage.yml文件中

amazon:
  service: S3
  access_key_id: zzz
  secret_access_key: zzz
  region: zzz
  bucket: zzz
  upload:
    acl: "public-read"

在代码中

attachment = ActiveStorage::Attachment.find(90)
attachment.blob.service_url # returns large URI
attachment.blob.service_url.sub(/\?.*/, '') # remove query params

它将返回如下内容: “ https://foo.s3.amazonaws.com/bar/buz/2yoQMbt4NvY3gXb5x1YcHpRa

由于上面的配置,它是公共可读的。

答案 3 :(得分:1)

感谢@genkilabs和@Aivils_Štoss,使用service_url方法结合剥离参数来获取公共URL是一个好主意!

但是,如果对大量文件使用此方法,则可能会涉及扩展问题。如果显示的是带有文件附件的记录列表。对于 每个 的调用,service_url会在您的日志中显示如下内容:

DEBUG -- : [8df9220c-e8c9-45b7-a1ee-b746e623ca1b]   S3 Storage (1.4ms) Generated URL for file at key: ...

您也不能急于加载这些调用,因此您可能会大量调用S3 Storage来为显示的每个记录生成这些URL。

我通过创建一个演示者来解决此问题:

class FilePresenter < SimpleDelegator
  def initialize(obj)
    super
  end

  def public_url
    return dev_url if Rails.env.development? || Rails.env.test? || assest_host.nil?

    "#{assest_host}/#{key}"
  end

  private

  def dev_url
    Rails.application.routes.url_helpers.rails_blob_url(self, only_path: true)
  end

  def assest_host
    @assest_host ||= ENV['ASSET_HOST']
  end
end

然后我用以下方法设置一个ENV变量ASSET_HOST

https://<your_app_bucket>.s3.<your_region>.amazonaws.com

然后,当我显示图像或仅显示文件链接时,我会这样做:

<%= link_to(image_tag(company.display_logo),
    FilePresenter.new(company.logo).public_url, target: "_blank", rel:"noopener") %>

<a href=<%= FilePresenter.new(my_record.file).public_url %> 
   target="_blank" rel="noopener"><%= my_record.file.filename %></a>

请注意,您仍然需要对图像使用display_logo,以便在使用它们时可以访问该变体。

此外,这一切都基于根据上述@genkilabs步骤#2将我的AWS存储桶设置为公共,并根据@Aivils_Štoss!的建议将upload: acl: "public-read"设置添加到我的'config / storage.yml'中。 。

如果有人发现此方法有任何问题或陷阱,请告诉我!对于我来说,这似乎很有用,因为它允许我显示公共URL,而无需为每个记录访问S3存储以生成该URL。

答案 4 :(得分:0)

有点晚了,但是您也可以像这样获得公共URL(假设Post模型带有一个附加的header_image,如上面的示例所示):

@post.header_image.service.send(:object_for, @post.header_image.key).public_url