Expected result
Force a file download even if the file is displayable in browser like an image.
Problem
I'm having troubles with using rails_blob_url(file, disposition: 'attachment')
or file.url(disposition: 'attachment')
which is probably the same? The response headers of the file are always content-disposition: inline; filename="image.png"; filename*=UTF-8''image.png
On my local dev machine (using local file storage) it works perfectly fine also for images - everything gets a forced download. On my staging env using google cloud storage it seems as if disposition attachment gets ignored. There is no metadata set with disposition on the GCS file so as the documentation states it should use the querystring provided (response-content-disposition & response-content-type). All files are private so we are using a signed_url.
I created a method to debug - basically using the same methods as active storage does internally..
def download_url
disposition = "attachment"
service = @file.service
expires_in = 300
if service.name == :google
gcs_config = Rails.application.config.active_storage.service_configurations['google']
storage = Google::Cloud::Storage.new(
project_id: gcs_config['project'],
credentials: gcs_config['credentials']
)
bucket = storage.bucket(gcs_config['bucket'])
blob = bucket.file(@file.key, skip_lookup: true)
content_disposition_formatted = ActionDispatch::Http::ContentDisposition.format(disposition: disposition, filename: @file.filename.sanitized)
args = {
expires: expires_in,
query: {
"response-content-disposition" => content_disposition_formatted,
"response-content-type" => @file.content_type
}
}
logger.info "X"*80
logger.info @file.filename
logger.info "X"*80
logger.info args.inspect
logger.info "X"*80
blob.signed_url(**args)
else
rails_blob_url(@file, disposition: disposition, expires_in: expires_in)
end
end
Any ideas how to force this? It should be possible - right?!
there is an activestorage bug for gcs when using direct upload in
activestorage/lib/active_storage/service/gcs_service.rb#headers_for_direct_upload
. it sets the content disposition metadata to inline and currently the only way to get the query params for content disposition working is to clear the content disposition metadata.
here is a bushhack method for clearing it:
def clear_content_disposition
return unless file.service.name == :google
gcs_config = Rails.application.config.active_storage.service_configurations['google']
storage = Google::Cloud::Storage.new(
project_id: gcs_config['project'],
credentials: gcs_config['credentials']
)
bucket = storage.bucket(gcs_config['bucket'])
blob = bucket.file(file.key, skip_lookup: true)
blob.content_disposition = nil
end