rails / rails

Ruby on Rails
https://rubyonrails.org
MIT License
56.64k stars 21.82k forks source link

Active Storage should allow custom filenames and extensions #54553

Closed searls closed 1 month ago

searls commented 1 month ago

At present, the key generated by ActiveStorage::Blob to determine the filename for an attachment is not user-configurable. The implementation looks like this:

  def key
    # We can't wait until the record is first saved to have a key for it
    self[:key] ||= self.class.generate_unique_secure_token(length: MINIMUM_TOKEN_LENGTH)
  end

My issue is that there are times when the URL actually matters, so it'd be nice to be able to customize this on an per-has_*_attached basis.

The issue I'm running into is admittedly a bit particular, but I don't think it's entirely unusual. I'm using Active Storage to store podcast files to be listed in an RSS feed. Apple is continually rejecting my RSS feed because they require the URL of each <enclosure> (read: audio file) to end in a supported file extension (e.g. .mp3). But I lack any control over these URLs (which, to illustrate, look like this: https://cdn.example.com/abcd26nq79yj5jqj3ub1kskik9876)

My app uses AWS S3 as the backend and it serves those assets with an AWS CloudFront CDN distribution that points to that S3 bucket as its origin.

As a result, my options are very limited. AFAICT, they are:

  1. Monkey-patch ActiveStorage::Blob#key, which seems fraught
  2. Respond with a query parameter (what I'm trying now) that ends in .mp3 (e.g. https://cdn.example.com/abcd26nq79yj5jqj3ub1kskik9876?filename=1.mp3) and hope Apple's parser is satisfied
  3. Proxy each request instead of generating direct URLs to the CDN, which would quickly saturate all my application servers while clients download large files
  4. Redirect each request to the CDN URL, which would (one presumes) result in many podcast clients attempting and failing to make HTTP byte range requests (another requirement for distribution in Apple's directory)

In my 18 months of using Active Storage, this is a good example of the sort of paper cut that makes the experience a real drag on my productivity, and seems like something the library could straightforwardly solve by exposing a way for users to modify a particular attachment's key-generation strategy (and taking on the responsibility of ensuring those keys be unique)

System configuration

Rails version: 8.0.1

Ruby version: 3.4.2

searls commented 1 month ago

Welp, for anyone running into this, I just traced back where the self[:key] came from, and it looks like it's user-assignable thanks to this commit

Example:

user.avatar.attach key: "avatars/#{user.id}.jpg", io: io, content_type: "image/jpeg", filename: "avatar.jpg"

Closing this