10
votes

I am building a website using Ruby on Rails. To upload images I am using Active Storage and Amazon S3. All's good here. Users can upload images and images are viewable on the website (images are public).

Now, in production, the url for images are: https://example.com/rails/active_storage/representations/1ej21h...

Which return a 302 to the S3 bucket: https://my-bucket.amazonaws.com/variants/9jdh2...

I am not a big fan of:

  • the two roundtrips to get the image ;
  • sending requests for images to the Rails server ;
  • the feeling of sluggishness on these images.

And I would rather like to use Cloudfront to serve these images.

I searched in Rails Guides, on Google and StackOverflow, but didn't find a proper answer so far.

Is there any solution at this time to use Cloudfront with Active Storage?

Edit: More context: Each image will be loaded 1000 times by minute at least under normal traffic and from different countries. I don't want to put the server under this pressure (it has other requests to process). And I want users to load these images as fast as possible. Hence Cloudfront as a the CDN for these images (public images, no need to get a signed url).

2
Is what you're referring to the fact that rails will hit the server to let active_job generate a new signed url to your bucket? This causes the images to blink on page load while the front end waits on the server to generate the signed url. There is a patch you can apply to fix this. Can you confirm if this is what youre talking about?Verty00
@Verty00 The image does not blink: its quite slow to load and the requests for images are processed by the rails server (indeed to generate a new signed url). I don't need any signing, images are public. When a user display a page, I want the url for images displayed by the server to be my-distribution.cloudfront.net/my-image-url.jpg. So the rails server does not need to process unnecessary requests for images (eg "bypassing signing").gpichot
Check out my answer below. Does this help?Verty00

2 Answers

1
votes

Try this...

in controllers/active_storage/representations_controller.rb <--create if it doesnt exist. You should put...

module ActiveStorage
  class RepresentationsController < BaseController
    include ActiveStorage::SetBlob

    def show
      expires_in 1.year, public: true
      variant = @blob.representation(params[:variation_key]).processed
      send_data @blob.service.download(variant.key),
            type: @blob.content_type || DEFAULT_SEND_FILE_TYPE,
            disposition: 'inline'
    end
  end
end

Then when you call an image using @model.image.variant(resize: '250x250') making sure to substitute your desired dimensions. This is a hack for now. This should be fixed by rails 6 release I think.

0
votes

I always put my load balancers behind a cloudfront distribution and I put the Route53 domain pointing to the distribution, not to the load balancer. This way you can cache any behavior you want. You can cache of course any request to rails/active_storage/representations/redirect/* and it will preserve any cache headers the application server returns in the first time the image is retrieved.