How to retrieve attachment url with Rails Active Storage with S3 How to retrieve attachment url with Rails Active Storage with S3 ruby-on-rails ruby-on-rails

How to retrieve attachment url with Rails Active Storage with S3


Use ActiveStorage::Blob#service_url. For example, assuming a Post model with a single attached header_image:

@post.header_image.service_url

Update: Rails 6.1

Since Rails 6.1 ActiveStorage::Blob#service_url is deprecated in favor of ActiveStorage::Blob#url.

So, now

@post.header_image.url

is the way to go.

Sources:


My use case was to upload images to S3 which would have public access for ALL images in the bucket so a job could pick them up later, regardless of request origin or URL expiry. This is how I did it. (Rails 5.2.2)

First, the default for new S3 bucked is to keep everything private, so to defeat that there are 2 steps.

  1. Add a wildcard bucket policy. In AWS S3 >> your bucket >> Permissions >> Bucket Policy
{    "Version": "2008-10-17",    "Statement": [        {            "Sid": "AllowPublicRead",            "Effect": "Allow",            "Principal": "*",            "Action": "s3:GetObject",            "Resource": "arn:aws:s3:::your-bucket-name/*"        }    ]}
  1. In your bucket >> Permissions >> Public Access Settings, be sure Block public and cross-account access if bucket has public policies is set to false

Now you can access anything in your S3 bucket with just the blob.key in the url. No more need for tokens with expiry.

Second, to generate that URL you can either use the solution by @Christian_Butzke: @post.header_image.service.send(:object_for, @post.header_image.key).public_url

However, know that object_for is a private method on service, and if called with public_send would give you an error. So, another alternative is to use the service_url per @George_Claghorn and just remove any params with a url&.split("?")&.first. As noted, this may fail in localhost with a host missing error.

Here is my solution or an uploadable "logo" stored on S3 and made public by default:

#/models/company.rbhas_one_attached :logodef 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

Enjoy ^_^


If you need all your files public then you must make public your uploads:

In file config/storage.yml

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

In the code

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

It will return something like:"https://foo.s3.amazonaws.com/bar/buz/2yoQMbt4NvY3gXb5x1YcHpRa"

It is public readable because of the config above.