21

Config Info

rails version 6.0
ruby version 2.7.0
gem 'image_processing', '~> 1.2'

storage.yml

local:
  service: Disk
  root: <%= Rails.root.join("storage") %>

development.rb

config.active_storage.service = :local
Rails.application.routes.default_url_options[:host] = 'localhost:3000'
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

This exception is coming only when I'm using service= :local. With AWS S3 config, on using :amazon it works fine.

user.rb model

  has_one_attached :avatar
  
  #throwing exception
  def avatar_urls
    {
      original: avatar.service_url
    } if avatar.attachment
  end

While accessing avatar_urls the exception(URI::InvalidURIError (bad URI(is not URI?): nil) ) is thrown. However, as If I change my avatars_url method to following, it works fine.

  #working method
  def avatar_urls
    {
      thumbnail: url_for(avatar.variant(resize: "100x100").processed)
    } if avatar.attachment
  end

Here is the trace:

Disk Storage (5056.7ms) Generated URL for file at key: variants/i5w1ie6ro07mib4qcdn30lmik6wn/2a7fa5dad6ac227a16e961cbd12ca6f35f1d7947f56a97754d5e22c1a0fd3372 ()
cb_app_container | Completed 500 Internal Server Error in 9523ms (ActiveRecord: 580.9ms | Allocations: 1185509)
cb_app_container | URI::InvalidURIError (bad URI(is not URI?): nil):
cb_app_container | app/models/user.rb:53:in `avatar_urls'
cb_app_container | app/models/user.rb:27:in `user_json'
cb_app_container | app/controllers/api/v1/users_controller.rb:13:in `update'
cb_app_container | [ActiveJob] [ActiveStorage::AnalyzeJob] [33448db4-cf54-4677-906c-06b59f1579ee]    (61.6ms)  BEGIN
cb_app_container | [ActiveJob] [ActiveStorage::AnalyzeJob] [33448db4-cf54-4677-906c-06b59f1579ee]   ActiveStorage::Blob Update (10.3ms)  UPDATE "active_storage_blobs" SET "metadata" = $1 WHERE "active_storage_blobs"."id" = $2  [["metadata", "{\"identified\":true,\"width\":1952,\"height\":3264,\"analyzed\":true}"], ["id", 45]]
cb_app_container | [ActiveJob] [ActiveStorage::AnalyzeJob] [33448db4-cf54-4677-906c-06b59f1579ee]    (19.6ms)  COMMIT
cb_app_container | [ActiveJob] [ActiveStorage::AnalyzeJob] [33448db4-cf54-4677-906c-06b59f1579ee] Performed ActiveStorage::AnalyzeJob (Job ID: 33448db4-cf54-4677-906c-06b59f1579ee) from Async(default) in 6916.06ms
Imran Ahmad
  • 2,798
  • 3
  • 28
  • 49

7 Answers7

23

I ran into this same issue in Rails 6.1, and found that I had to include a special concern in my controller to make ActiveStorage aware of the current host:

module Api
  module V1
    class ApiController < ActionController::API
      # Make ActiveStorage aware of the current host (used in url helpers)
      include ActiveStorage::SetCurrent
    end
  end
end

4

I ended up including include Rails.application.routes.url_helpers in the controller and used rails_blob_url(avatar.image). I also aded this line in the routes file. I will refactor it later.

Rails.application.routes.default_url_options[:host] = 'localhost:3000'

Laah Lima
  • 41
  • 2
3

I ended up using url_for:

Rails.application.routes.url_helpers.url_for(Event.last.image)
Dorian
  • 7,749
  • 4
  • 38
  • 57
3

For anyone on Rails 5.x running into this issue, you need to define the host for the service_url. This is the code that ActiveStorage uses: https://github.com/rails/rails/blob/v5.2.6/activestorage/app/controllers/active_storage/base_controller.rb

The key line is: ActiveStorage::Current.host = request.base_url

Stick that before calling avatar.service_url

Big caveat: You probably don't want to be calling avatar.service_url directly. url_for(...) automatically deals with all this plumbing for you.

mattwise
  • 1,464
  • 1
  • 10
  • 20
1

Simplest way for me was add this to application_controller.rb

# app/controllers/application_controller.rb
include ActiveStorage::SetCurrent

It's 100% from @RemonOldenbeuving's answer, just a little simpler.

stevec
  • 41,291
  • 27
  • 223
  • 311
0

According to the documentation https://api.rubyonrails.org/classes/ActiveStorage/Variant.html on processed.service_url: This will create and process a variant of the avatar blob that's constrained to a height and width of 100. Then it'll upload said variant to the service according to a derivative key of the blob and the transformations.

Also: https://api.rubyonrails.org/classes/ActiveStorage/Variant.html#method-i-service_url

Looking at the code at GH I can see the service comes from https://github.com/rails/rails/blob/fbe2433be6e052a1acac63c7faf287c52ed3c5ba/activestorage/app/models/active_storage/blob.rb which represents a web service, in this case S3.

I think (with the fear of being wrong and lose some points) that you shouldn't use it at local, only on prod as you said with S3. Seems like Disk is not considered as a service (for obvious reasons), have to dig more into the code (just dealing with this issue right now, it works if I set <%= image_tag category.image.variant(resize_to_fill: [400, 400]) %> but fails on <%= image_tag category.image.variant(resize_to_fill: [400, 400]).processed.service %>, on localhost, but I can access to .processed, is just .service that fails).

Did you do anything else to make it work on local? I guess you can also add a condition to check the env in which it's running.

pablomarti
  • 2,087
  • 2
  • 22
  • 35
0

better way to get the url is explained in this answer: https://stackoverflow.com/a/53547638

Rails.application.routes.url_helpers.rails_representation_url(picture_of_car.variant(resize: "300x300").processed, only_path: true)

Iuri G.
  • 10,460
  • 4
  • 22
  • 39