Allow setting Content-Encoding when uploading a file with Active Storage

Active Storage lets you set content_type and content_disposition per file on upload, but there is no similar option for Content-Encoding. The S3 service accepts upload: options in storage.yml, but those apply to all the files in the service, and in our case only some files should get the header.

Some context: on https://openstreetmap.org we are working on gzipping plain GPX traces before storing them on S3. With Content-Encoding: gzip on the object, S3 serves the compressed file and the browser decompresses it, so the user downloads the same file they uploaded and the server does no extra work. Users also upload zip and bzip2 files, and those must not get the header, so the static option doesn’t help us.

To make it work we wrote a custom S3 service that overrides the internal upload methods: https://github.com/openstreetmap/openstreetmap-website/pull/7124. It works, but it copies internal code and may break silently with a Rails update, so it’s not something we want to maintain long term. Someone had the same problem in 2020 and the only answer was to monkey patch the blob model and the service: How to set content-encoding metadata when uploading a file with ActiveStorage?

For reference, other Ruby upload gems like CarrierWave and Shrine already allow setting this per file. It would be great to have something similar built into Active Storage, and maybe the same mechanism could support other per-file headers too, like cache_control or content_language. For our case, content_encoding would help solve it.

Is this something that could fit in Active Storage?