ActiveStorage service_url && rails_blob_path cannot generate full url when not using S3

tgf picture tgf · Jun 30, 2018 · Viewed 9.9k times · Source

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?

Answer

George Claghorn picture George Claghorn · Jun 30, 2018

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