Proposal: Enhance the DirectUpload JS API to support more useful custom blob names (like the ActiveStorage::Attached::*attach(:key) argument)

TL;DR

It Would Be Nice If™ if DirectUpload accepted a key argument to customize and override the default hash-based blob naming and to support sub-dirs within buckets.

(Others have asked for this before, e.g. in this thread that began in 2022))

Proposal

The Edge Rails docs illustrate how ActiveStorage::Attached::Many#attach accepts an optional :key argument to name and organize blobs (section 3.3 Attaching File/IO Objects). As the docs describe,

AWS S3 otherwise uses a random key to name your files. [Specifying a key] is helpful if you want to organize your S3 Bucket files better.

I :heart: this feature of ActiveStorage in Rails — in my experience, legible filenames + directory structures can be quite helpful in debugging or troubleshooting scenarios. An opaque tree of checksum hashes feels a little more fragile to me personally.

But for DirectUpload, I wasn’t able to find docs in support of equivalent customization options for JS use cases (e.g. from a Vue or Svelte or React app with a Rails back-end). Does the JS DirectUpload api support passing a custom key function the way the Rails one does?

Context

Mapping files to specific ActiveStorage sub-directories and/or specific file/blob naming schemas is supported and can be helpful eg. for some use cases, it may be valuable to preserve the filename, or to organize by date, or whatever.

For example, the following s3 key method:

  • preserves the original input filename
  • adds a statistically unique-ish suffix (not saying it’s great code, just an example)
  • organizes files into two-digit year / two-digit month directories for archival purposes (and easier reverse-engineering in the case of a debugging scenario):
def attach_image(img_path)
  images.attach(
    io: File.open(img_path),
    filename: File.basename(img_path),
    key: image_storage_key(img_path)
  )
end

private

def image_storage_key(img_path)
  filename = File.basename(img_path, ".*")
  ext = img_path.match( %r{\w{3,4}$} )
  t = Date.today
  month_year = t.year.to_s.slice(2,2) + "%02d" % t.month
  "#{month_year}-#{filename}-#{SecureRandom.hex.slice(0,6)}.#{ext}"
end

System configuration

Rails version: 7.2.2

1 Like