Active Storage with Amazon S3 not saving with filename specified but using file key instead
Asked Answered
M

4

10

I am having an issue with Active Storage. When I upload to Amazon S3, instead of saving the file inside the bucket with the original name like myfile.zip it is saving it as the key which is associated with that file. So in Cyberduck I am seeing something like this: 5YE1aJQuFYyWNr6BSHxhQ48t. Without any file extension.

I am not sure if there is some setting in Rails 5 or whether it is within Amazon S3 but I have spent hours Googling around to figure out why this is happening.

Any pointers would be really appreciated!

Best regards, Andrew

Maxie answered 1/6, 2018 at 9:27 Comment(3)
I haven't worked with Cyberduck, but if you write a script to download files from a bucket, then Ruby's File class and FileUtils module module can be helpful for translating keys to file paths and for capturing the file name.Cholecystotomy
Many thanks @GinnieHench, this is for the download side of things, but I was just wondering whether it was possible to change the actual filename inside the S3 bucket itself. To be the original filename.Maxie
@Drew, it isn’t.Bushmaster
T
9

This is by design, from ActiveStorage. The file is stored by it's key and without extension on S3, but when the URL is generated by ActiveStorage, the disposition and filename are set.

def url(key, expires_in:, filename:, disposition:, content_type:)
  instrument :url, key: key do |payload|
    generated_url = object_for(key).presigned_url :get, expires_in: expires_in.to_i,
      response_content_disposition: content_disposition_with(type: disposition, filename: filename),
      response_content_type: content_type

    payload[:url] = generated_url

    generated_url
  end
end

This is probably done to avoid filename escaping issues that you'd run into otherwise.

You can read more about the Content-Disposition headers here.

Trefor answered 1/6, 2018 at 18:48 Comment(2)
Wow I had no idea! Thanks so much! I did wonder if that was by design!Maxie
Using key without file extension breaks caching with cloudflare cdn (since cloudflare uses a whitelist of extensions to cache files).Borer
F
6

You can still reference the name with filename accessor.

class User < ApplicationRecord
  has_one_attached :photo
  ...
end

filename = User.first.photo.filename
Frink answered 3/9, 2018 at 4:27 Comment(0)
E
5

In order to have a custom filename on S3, you should update both blob.key and the name on S3.

Active storage uploads images on S3 using blob.key as remote image path and name.

For my usage, I only changed the name for 'images variants' with a Monkey Patch that allows to generate a key terminating by the filename :

config/initializers/active_storate_variant.rb :

ActiveStorage::Variant.class_eval do
  def key
    "variants/#{blob.key}/#{Digest::SHA256.hexdigest(variation.key)}/#{filename}"
  end
end

So when I need the public url for an image variant, I just call image.url('400x400')

This is how my Image model is customized :

class Image < ApplicationRecord
  belongs_to :imageable, polymorphic: true
  has_one_attached :picture

  SIZES = { '400x400' => '400x400' }

  def url(size)
    return "https://placehold.it/#{size}" unless picture.attached?

    'https://my_s3_subdomain.amazonaws.com/' +
        picture.variant(resize: SIZES[size]).processed.key
  end

  ...

end

If someone has a better way to do that, I would be happy to see it :)

Exeunt answered 24/1, 2020 at 9:53 Comment(0)
T
0

You could monkey patch the key method with something like this in an intializer:

Rails.configuration.to_prepare do
  ActiveStorage::Blob.class_eval do
    def key
      # Change to what you need
      self[:key] ||= "#{self.class.generate_unique_secure_token(
        length: self.class::MINIMUM_TOKEN_LENGTH
      )}.#{self.filename.extension}"
    end
  end
end

You can check the original method here: https://github.com/rails/rails/blob/main/activestorage/app/models/active_storage/blob.rb

Touzle answered 21/8 at 3:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.