25

I have a basic ActiveStorage setup with one model that has_many_attached :file_attachments. In a service elsewhere I'm trying to generate a link to be used outside the main app (email, job etc).

With S3 in production I can do: item.file_attachments.first.service_url and I get an appropriate link to the S3 bucket+object.

I cannot use the method prescribed in the rails guides: Rails.application.routes.url_helpers.rails_blob_path(item.file_attachments.first)

It errors with: ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true I can pass it a host: 'http://....' argument and it's happy although it still doesn't generate the full URL, just the path.

In development I'm using disk backed file storage and I can't use either method:

> Rails.application.routes.url_helpers.rails_blob_path(item.file_attachments.first)
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true

Setting host here also doesn't generate a full URL.

In production service_url works, however here in development I get the error:

> item.file_attachments.first.service_url
ArgumentError: Missing host to link to! Please provide the :host parameter, set default_url_options[:host], or set :only_path to true

and specifying a host doesn't help:

item.file_attachments.first.service_url(host:'http://localhost.com')
ArgumentError: unknown keyword: host

I've also tried adding

config.action_mailer.default_url_options = { :host => "localhost:3000" }
config.action_storage.default_url_options = { :host => "localhost:3000" }
Rails.application.routes.default_url_options[:host] = 'localhost:3000'

with no success.

My question is - how can I get the full URL in a manner that works in both development and production? or where do I set the host at?

1
  • Here there are more informations about this subject github issue Commented Jan 28, 2021 at 12:44

4 Answers 4

47

Active Storage’s disk service expects to find a host for URL generation in ActiveStorage::Current.host.

When you call ActiveStorage::Blob#service_url manually, ensure ActiveStorage::Current.host is set. If you call it from a controller, you can subclass ActiveStorage::BaseController. If that’s not an option, set ActiveStorage::Current.host in a before_action hook:

class Items::FilesController < ApplicationController
  before_action do
    ActiveStorage::Current.host = request.base_url
  end
end

Outside of a controller, use ActiveStorage::Current.set to provide a host:

ActiveStorage::Current.set(host: "https://www.example.com") do
  item.file_attachments.first.service_url
end
Sign up to request clarification or add additional context in comments.

8 Comments

Thank you sir! I got it working with this info. Can I ask a follow up - if I don't want direct links to S3 is there a similar way to get a long lasting URL through the /rails/active_storage (redirection) endpoint?
Thank you, this saved me. I believe Action Storage and Action Mailer need update in docs since this is as I understand the only way to generate images inside an email body so email clients show them. Basically if to do it regular way, images will not be shown since actual service_url is required by email clients/service providers.
Very helpful for model testing!
"include ActiveStorage::SetCurrent" in the controller will do the same thing as the sample above.
In Rails 7.1, you have to do ActiveStorage::Current.set(url_options: { host: "https://www.example.com" }) { <code here> } instead
|
5

I needed the url for an image stored in ActiveStorage.

The image was

Post.first.image

Wrong

Post.first.image.url
Post.first.image.service_url

Right

url_for(Post.first.image)

3 Comments

They are different, though. The .url will return the service URL if the object is public. The url_for will return the redirect path.
@Daniel Thanks for pointing this out, what exactly is the redirect path? (I was using the above methods to provide urls to the objects to an external site, so I think it did what I needed, but I will clarify it in the answer if it could confuse others)
The url_for will return something like: http://www.example.com/rails/active_storage/blobs/redirect/xxxxxxxxx/thurman_senger.jpeg whereas the .url will return https://my_bucket.s3.ap-northeast-1.amazonaws.com/xxxxxxxxxxxxxxx?lots_of_things_if_the_bucket_is_private. If you are serving the URL of the assets through an API, you may want to serve the S3 URL directly to avoid extra hits to your server. Moreso if the bucket is public.
1

In Rails 7.1 ActiveStorage::Current#host and ActiveStorage::Current#host= methods are removed

But it's possible to use ActiveStorage::SetCurrent module, it sets ActiveStorage::Current.url_options before action, so just enough to include in your controller (for example in ApplicationController)

include ActiveStorage::SetCurrent

This module source:

module ActiveStorage::SetCurrent
  extend ActiveSupport::Concern

  included do
    before_action do
      ActiveStorage::Current.url_options = { protocol: request.protocol, host: request.host, port: request.port }
    end
  end
end

Of course you can set these options yourself

ActiveStorage::Current.url_options = { host: 'my.host' } # and other options

1 Comment

If doing this outside of a controller in 7.1 (eg in a model), you can do the following: ActiveStorage::Current.set(url_options: { host: "http://localhost:3000" }) { <code here> }
0

I had a similar problem. I had to force the port to be different due to a container setup. In my case the redirect done by ActiveStorage included the wrong port. For me adjusting the default_url_options worked:

before_action :configure_active_storage_for_docker, if: -> { Rails.env.development? }

def configure_active_storage_for_docker
  Rails.application.routes.default_url_options[:port] = 4000
end

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.