The public option is added to Active Storage in this PR: https://github.com/rails/rails/pull/36729
In it he clearly says: "In the public bucket, the directory structure is /[key]/[filename]"
Which makes sense and is exactly what I want. I want to be able (for example) to email the link to someone and allow them to download the file. So I need the filename.extension. But when I spin up a Rails 6.1 app the uploads to my bucket do not have the filename after them. The files do appear in my bucket, but only as their key. Not key/filename.
amazon:
service: S3
access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %>
secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %>
region: us-east-1
bucket: mybucket
public: true
The uploading works fine. The files are uploaded and appear in my bucket. But in the view <%= @user.avatar.url %>
returns https://s3.amazonaws.com/mybucket/g3ci2umbfj6wkxyggx7arhekxfib
I want it to return https://s3.amazonaws.com/mybucket/g3ci2umbfj6wkxyggx7arhekxfib/myfile.png
This is really annoying me because in the PR the author clearly states that the public files are saved as key/filename.extension
So the question: In Rails 6.1 does the url
method return the filename as part of the path or not. And if not why does the author say that it does? And if not is there a better way than patching key
?
TLDR:
will give you a download link like
https://corsego-production.s3.eu-central-1.amazonaws.com/3gbpl68kckpkyrbjoslsl254th0u?response-content-disposition=inline&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEGgaCmV1LBuuKkri8zL3ohM4h9STzhTsnavAgulrcpBavL0POXg%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20201002T170820Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=ASIA5RINJ20201002%2Feu-central-1%2Fs3%2Faws4_request&X-Amz-Signature=0a205d713ebaa9d3ca9d62
@user.avatar.url
is not supposed to return anything!@user.avatar
is supposed to return something like#<ActiveStorage::Attached::One:0x00007f15986ee4c0>
When creating an attachment with Active Storage, the following fields about the attachment are automatically populated and saved as
active_storage_blobs
:Let's say you upload an image with active storage. The above Table will be populated in a following way:
To display the user avatar as an image, you must call
= image_tag @user.avatar
= rails_blob_url(@user.avatar)
will give you something likehttps://example.com/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--5fb10a9c8e9f2c4e7099eee21c1dc2ff0343c210/Screenshot%202020-10-02%20125405.png
= @user.avatar
will give you something like#<ActiveStorage::Attached::One:0x00007f1589293dd0>
= url_for(@user.avatar)
will give you something like/rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBCZz09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--5fb10a9c8e9f2c4e7099eee21c1dc2ff0343c210/Screenshot%202020-10-02%20125405.png
The second table in the migrations created by active_storage is this:
basically it connects the blob with a specific record in your application (basically the
avatar
is ablob
, the@user
is therecord
, and they are associated via theactive_storage_attachments
table.So you can also run the following commands on the @user.avatar method:
Now, below is an example url to the above file stored on AWS S3: https://corsego-production.s3.eu-central-1.amazonaws.com/3gbpl68kckpkyrbjoslsl254th0u?response-content-disposition=inline&X-Amz-Security-Token=IQoJb3JpZ2luX2VjEGgaCmRzBFAiBSJ2QIEqs1opj%2BuCR74CDMt67ueDTTQIhAOJGGy2wfmxmGUwpQe9cyc84ZhUhuWKHdVgTUctbtGrVKdinkpg0w7OikcyNYpnbq%2FefcTmEgRvIlPO%2B0itFxUr8mKUvnDYSuKkri8zL3ohM4h9STzhTsnavAgulrcpBavL0POXg%3D%3D&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Date=20201002T170820Z&X-Amz-SignedHeaders=host&X-Amz-Expires=300&X-Amz-Credential=ASIA5RINJLY2F20201002%2Feu-central-2%2Fs3%2Faws4_request&X-Amz-Signature=0a205d713ebaaa339ad4ee09db9dfe16986e69d3ca9d62
See how the url contains a secret token and an expiry time? It is advised not to have permanent urls to files.
P.S. When connecting AWS S3 to your application, don't forget to add CORS configuration: