问题
If you use ActiveStorage and you have a page with N images you get N additional requests to your Rails app (i.e. N redirects). That means wasting a lot of server resources if you have tens of images on a page.
I know that the redirect is useful for signed URLs. However I wonder why Rails does not precompute the final signed URL and embed that into the HTML page... In this way we could keep the advantages of signed URLs / protected files, without making N additional calls to the Rails server.
Is it possible to include the final URL / pre-signed URL of image variants directly in the HTML (thus avoiding the redirect)? Otherwise, why is that impossible?
回答1:
You can use the service_url to create direct links to your resources.
We don't use Rails views in our project so my knowledge about the view layer is rusty. I think you could put it in a dedicated helper and then use it from your views.
回答2:
After days of reasoning and tests, I am really excited of my final solution, which I explain below. This is an opinionated approach to images and may not represent the current Rails Way™️, however it has incredible advantages for websites that serve many public images, in particular:
- When you serve a page with N images you don't get 1 + N requests to your app server, instead you get only 1 request for the page
- The images are served through a CDN and this improves the loading time
- The bucket is not completely public, instead it is protected by Cloudflare
- The images are cached by Cloudflare, which greatly reduce your S3 bill
- You greatly reduce the number of API requests (i.e. exists) to S3
- This solution does not require large changes to Rails, and thus it is straightforward to switch back to Rails default behavior in case of problems
Here's the solution:
- Create an s3 bucket and configure it to host a public website (i.e. call it
storage.example.com
) - you can even disable the public access at bucket level and allow access only to the Cloudflare ips using a bucket policy - Go to Cloudflare and configure a CNAME for
storage.example.com
that points to your domain; you need to use Flexible SSL (you can use a page rule for the subdomain); use page rules to set heavy caching: set Cache Everything and set a very long value (e.g. 1 year) for Browser Cache TTL and Edge Cache TTL - In you Rails application you can keep using private storage / acl, which is the default Rails behavior
- In your Rails application call
@post.variant(...).processed
after every update or creation of@post
; then in your views use'https://storage.example.com/' + @post.variant(...).key'
(note that we don't callprocessed
here in the views to avoid additional checks in s3); you can also have a rake task that callsprocessed
on each object, in case you need to regenerate the variants; this is works perfectly if you have only a few variants (e.g. 1 image / variant per post) that are changed infrequently
Most of the above steps are optional, so you can combine them based on your needs.
来源:https://stackoverflow.com/questions/59013049/rails-activestorage-how-to-avoid-one-redirect-for-each-image