2
votes

I use ActiveStorage with the default Disk service. My api returns the image URL and in the frontend (Vue.js) the image is embedded via an IMG tag. When I open the image URL the image is displayed correctly.

render json: {
  image_url: rails_blob_path(user.photo, only_path: true),
  ....
}
<a :href="image_url">
  <img :src="image_url">
</a>

When I look at the source code, Safari keeps trying to call the old URL. This is no longer possible after 5 minutes. I have tried to set config.active_storage.service_urls_expire_in directly in the initializer, but this has no effect:

# config/initializers/active_storage.rb
Rails.application.config.active_storage.service_urls_expire_in = 1.week

When I place the image default rails way the image gets a different URL and this is not expire:

image_tag(user.photo)
# => <img src=".../rails/active_storage/disk/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaDdDRG9JYTJWNVNTSWhjM2w1WlhSbGNtMWxOVEJvWW1kdE1YRm9ObW96TjNvd2IzTjNjd1k2QmtWVU9oQmthWE53YjNOcGRHbHZia2tpYjJsdWJHbHVaVHNnWm1sc1pXNWhiV1U5SW1wdlkzSmZhV052Ymw5amFHRjBYMnB2WTNJdWFHbHNablJmWW14aGRTNXdibWNpT3lCbWFXeGxibUZ0WlNvOVZWUkdMVGduSjJwdlkzSmZhV052Ymw5amFHRjBYMnB2WTNJdWFHbHNablJmWW14aGRTNXdibWNHT3daVU9oRmpiMjUwWlc1MFgzUjVjR1ZKSWc1cGJXRm5aUzl3Ym1jR093WlUiLCJleHAiOiIyMDIwLTA3LTAzVDA5OjQ4OjQyLjc0NFoiLCJwdXIiOiJibG9iX2tleSJ9fQ==--d23e3bb8161f54cce48a3d13a60d906c18574569/user_photo.png" />
rails_blob_path(user.photo, only_path: true)
# => ".../rails/active_storage/blobs/eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBkQT09IiwiZXhwIjpudWxsLCJwdXIiOiJibG9iX2lkIn19--23e3b6283c346c75d7e6c1e769eacb6d428fec0e/user_photo.png"

Is it possible that I use the URL of the image_tag in the api ?

Ruby version: 2.7.0

Rails version: 6.0.2.2

2
try using user.photo.service_url. It worked for me90srebel

2 Answers

3
votes

I had the same Problem, so I ended with following solution.

There might be a difference to your system, cause I have an Image model for each image, to be able to reuse an image for other entries, too.

So I added a unique token to the Image model and a media-route.
GET https://my-domain.com/media/thumb/V8sPcDSNmMqrCRybXYLVRpoR

media_controller:

def show
  return head :not_found if origin.blank?

  send_file path, type: origin.content_type, disposition: :inline
rescue ActionController::MissingFile, NoMethodError
  redirect_to fallback
end

private

def origin
  @origin ||= Image.find_by(token: params[:token])
end

def path
  @path ||= origin.local_path(params[:size])
end

...

Image:

...

def local_path(size = :medium)
  img = image_with_size(size)
  ActiveStorage::Blob.service.path_for(img.key) if img.present?
end

def content_type
   file.blob&.content_type || 'image/png'
end

...

image_with_size just returns the requested variant of a file.
Maybe my approach is helpful also in your system.

0
votes

In case you need to get the service url instead of the representation url (by url_for or image_tag), use the service_url method:

user.photo.service_url

And for variants:

user.photo.variant(resize_to_limit: [320, nil]).processed.service_url

The processed method ensures the variant transformation applied to the original is created and uploaded to the service.

If you're using a local Disk service, then the ActiveStorage::Current.host should be set manually, or the ActiveStorage::SetCurrent concern should be included in the custom controller that calls the method.

But there is a debate about exposing the service url https://github.com/rails/rails/issues/31419, and Rails recommends using url_for(variant).