32
rails version 5.2

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

我很难获得实际的文件 URL。我试过rails_blob_url了,但它给了我以下

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

如何通过 Sidekiq 访问真实文件 URL?

存储.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'

发展.rb

  config.active_storage.service = :development

我可以在 Web 界面上访问这些,但不能在 Sidekiq 中访问

4

7 回答 7

76

使用ActiveStorage::Blob#service_url. 例如,假设一个Post带有单个附加的模型header_image

@post.header_image.service_url

更新:Rails 6.1

由于 Rails 6.1ActiveStorage::Blob#service_url已被弃用,取而代之的是ActiveStorage::Blob#url.

所以,现在

@post.header_image.url

是要走的路。

资料来源:

于 2018-04-12T18:04:54.330 回答
15

我的用例是将图像上传到 S3,它可以公开访问存储桶中的所有图像,以便工作可以稍后获取它们,而不管请求来源或 URL 是否过期。我就是这样做的。(轨道 5.2.2)

首先,新 S3 bucked 的默认设置是将所有内容保密,因此需要 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

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

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

但是,要知道 object_for 是 on 的私有方法service,如果使用 public_send 调用会报错。因此,另一种选择是使用@George_Claghorn 的 service_url 并删除任何带有url&.split("?")&.first. 如前所述,这可能会在 localhost 中失败,并出现主机丢失错误。

这是我的解决方案或存储在 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

欣赏^_^

于 2019-03-14T22:29:15.623 回答
14

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

在文件 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

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

于 2019-09-06T13:16:32.213 回答
1

使用该service_url方法结合对参数进行条带化以获得公共 URL 是个好主意,感谢@genkilabs 和@Aivils_Štoss!

但是,如果您在大量文件上使用此方法,则会涉及潜在的缩放问题,例如。如果您要显示附有文件的记录列表。对于您的每次呼叫,service_url您的日志中都会看到如下内容:

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

您也不能急切加载这些调用,因此您可能会对 S3 存储进行大量调用,以便为您显示的每条记录生成这些 URL。

我通过创建这样的 Presenter 来解决它:

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 存储桶设置为公开,并upload: acl: "public-read"按照 @Aivils_Štoss! 的建议将设置添加到我的“config/storage.yml”中。

如果有人发现这种方法有任何问题或陷阱,请告诉我!这对我来说似乎很有用,它允许我显示一个公共 URL,但不需要为每条记录点击 S3 存储来生成该 URL。

于 2020-03-25T21:56:44.657 回答
0

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

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

2020-04-06 更新

  1. 您需要确保使用公共 ACL 保存文档(例如将默认设置为公共)

  2. rails_blob_url 也可用。请求将由 Rails 处理,但是,这些请求可能会很慢,因为需要在每个请求上生成一个私有 URL。(仅供参考:在控制器之外,您也可以像这样生成该 URL Rails.application.routes.url_helpers.rails_blob_url(@post, only_path: true):)

于 2019-02-27T12:54:56.827 回答
0

我在让它工作时遇到了一些问题。以为我会为后代记录它们。

  • 在 Rails 6.0 中使用@post.header_image.service_url
  • 在 rails >= 6.1 使用@GeorgeClaghorn@post.header_image.url建议。

我收到了这个错误:

error: uninitialized constant Analyzable

这是 rails 6.0 中的一个奇怪的错误,可以通过将其放置在 config/application.rb 中来修复

config.autoloader = :classic

然后我看到这个错误:

URI::InvalidURIError (bad URI(is not URI?): nil) Active Storage service_url

只需将其添加到您的 application_controller.rb即可修复它

include ActiveStorage::SetCurrent

现在像@post.image.blob.service_url 这样的东西会像你期望的那样工作=)

于 2022-02-18T13:22:35.737 回答
0

另请参阅rails active storage 中的公共访问。这是在 Rails 6.1 中引入的。

public: true在应用程序的 config/storage.yml 中指定。公共服务将始终返回一个永久 URL。

于 2022-02-24T04:41:24.727 回答