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
中访问答案 0 :(得分:23)
使用ActiveStorage::Blob#service_url
。例如,假设Post
模型附加了一个header_image
:
@post.header_image.service_url
答案 1 :(得分:3)
我的用例是将图像上传到S3,该桶可以对存储桶中的所有图像进行公共访问,因此无论请求的来源或URL到期如何,作业都可以在以后拾取它们。这就是我做的。 (Rails 5.2.2)
首先,将新S3降压的默认设置是将所有内容都设为私有,因此要失败就必须进行2步操作。
{
"Version": "2008-10-17",
"Statement": [
{
"Sid": "AllowPublicRead",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
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