Get path to ActiveStorage file on disk
Asked Answered
V

4

58

I need to get the path to the file on disk which is using ActiveStorage. The file is stored locally.

When I was using paperclip, I used the path method on the attachment which returned the full path.

Example:

user.avatar.path

While looking at the Active Storage Docs, it looked like rails_blob_path would do the trick. After looking at what it returned though, it does not provide the path to the document. Thus, it returns this error:

No such file or directory @ rb_sysopen -

Background

I need the path to the document because I am using the combine_pdf gem in order to combine multiple pdfs into a single pdf.

For the paperclip implementation, I iterated through the full_paths of the selected pdf attachments and load them into the combined pdf:

attachment_paths.each {|att_path| report << CombinePDF.load(att_path)}
Vogue answered 14/5, 2018 at 22:47 Comment(2)
The docs indicate that for blob_path "upon access, a redirect to the actual service endpoint is returned. This indirection decouples the public URL from the actual one" so by design this will foil what you are doing. Perhaps investigate using the download option.Handout
The disk service implementation has a method called path_for that does what you're looking for but it is private. So using #send to get the paths or going through the download-to-temp-files process seem to be the options.Ashby
V
12

Thanks to the help of @muistooshort in the comments, after looking at the Active Storage Code, this works:

active_storage_disk_service = ActiveStorage::Service::DiskService.new(root: Rails.root.to_s + '/storage/')
active_storage_disk_service.send(:path_for, user.avatar.blob.key)
  # => returns full path to the document stored locally on disk

This solution feels a bit hacky to me. I'd love to hear of other solutions. This does work for me though.

Vogue answered 15/5, 2018 at 14:44 Comment(1)
Small improvement ActiveStorage::Blob.service.send(:path_for, user.avatar.blob.key)Hallow
S
76

Use:

ActiveStorage::Blob.service.path_for(user.avatar.key)

You can do something like this on your model:

class User < ApplicationRecord
  has_one_attached :avatar

  def avatar_on_disk
    ActiveStorage::Blob.service.path_for(avatar.key)
  end
end
Stanger answered 6/8, 2018 at 13:54 Comment(1)
This is the 10th SO question I find but the only one that works. Thanks!Oly
K
47

I'm not sure why all the other answers use send(:url_for, key). I'm using Rails 5.2.2 and path_for is a public method, therefore, it's way better to avoid send, or simply call path_for:

class User < ApplicationRecord
  has_one_attached :avatar

  def avatar_path
    ActiveStorage::Blob.service.path_for(avatar.key)
  end
end

Worth noting that in the view you can do things like this:

<p>
  <%= image_tag url_for(@user.avatar) %>
  <br>
  <%= link_to 'View', polymorphic_url(@user.avatar) %>
  <br>
  Stored at <%= @user.image_path %>
  <br>
  <%= link_to 'Download', rails_blob_path(@user.avatar, disposition: :attachment) %>
  <br>
  <%= f.file_field :avatar %>
</p>
Kor answered 24/12, 2018 at 1:18 Comment(0)
V
12

Thanks to the help of @muistooshort in the comments, after looking at the Active Storage Code, this works:

active_storage_disk_service = ActiveStorage::Service::DiskService.new(root: Rails.root.to_s + '/storage/')
active_storage_disk_service.send(:path_for, user.avatar.blob.key)
  # => returns full path to the document stored locally on disk

This solution feels a bit hacky to me. I'd love to hear of other solutions. This does work for me though.

Vogue answered 15/5, 2018 at 14:44 Comment(1)
Small improvement ActiveStorage::Blob.service.send(:path_for, user.avatar.blob.key)Hallow
F
8

You can download the attachment to a local dir and then process it.

Supposing you have in your model:

has_one_attached :pdf_attachment

You can define:

def process_attachment      
   # Download the attached file in temp dir
   pdf_attachment_path = "#{Dir.tmpdir}/#{pdf_attachment.filename}"
   File.open(pdf_attachment_path, 'wb') do |file|
       file.write(pdf_attachment.download)
   end   

   # process the downloaded file
   # ...
end
Footie answered 13/6, 2018 at 13:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.