shrine icon indicating copy to clipboard operation
shrine copied to clipboard

content-disposition not being set

Open masudhossain opened this issue 3 years ago • 3 comments

Can't tell what i'm doing wrong. Here's my shrine.rb below. The resulting content-dispostion stays default, which is inline; filename="Screenshot at Feb 09 10-09-57.png"; filename*=UTF-8''filenamepng

require "shrine/storage/s3"

s3_options = {
  access_key_id:     Rails.application.secrets.digitalocean_spaces_key,
  secret_access_key: Rails.application.secrets.digitalocean_spaces_secret,
  region:            Rails.application.secrets.digitalocean_spaces_region,
  endpoint:          'https://nyc3.digitaloceanspaces.com',
  bucket:            Rails.application.secrets.digitalocean_spaces_bucket,
}

Shrine.storages = {
  # Shrine::Storage::S3.new(public: true, **s3_options)
  cache: Shrine::Storage::S3.new(public: true, prefix: "cache", upload_options: {acl: "public-read"}, **s3_options),
  store: Shrine::Storage::S3.new(public: true, prefix: "store", upload_options: {acl: "public-read"}, **s3_options),
}
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data # for retaining the cached file across form redisplays
Shrine.plugin :restore_cached_data # re-extract metadata when attaching a cached file
Shrine.plugin :uppy_s3_multipart # load the plugin
Shrine.plugin :backgrounding # adds background processing

Shrine.plugin :presign_endpoint, presign_options: -> (request) do
  # Uppy will send the "filename" and "type" query parameters 
  filename = request.params["filename"]
  type     = request.params["type"]
 
  { 
    content_disposition: ContentDisposition.attachment(filename), # download with original filename 
    content_type: type,                                # set correct content type 
  }
end

masudhossain avatar Feb 17 '22 18:02 masudhossain

It would appear that it might get overwritten here when copying the cached file to permanent storage. You should be able to use the upload_options plugin to set the attachment content disposition on upload to permanent storage.

As this doesn't appear to be a bug in the content_disposition gem, I will move this issue to the Shrine repository.

janko avatar Feb 17 '22 23:02 janko

It would appear that it might get overwritten here when copying the cached file to permanent storage. You should be able to use the upload_options plugin to set the attachment content disposition on upload to permanent storage.

As this doesn't appear to be a bug in the content_disposition gem, I will move this issue to the Shrine repository.

require "shrine/storage/s3"
require "content_disposition"

s3_options = {
  access_key_id:     Rails.application.secrets.digitalocean_spaces_key,
  secret_access_key: Rails.application.secrets.digitalocean_spaces_secret,
  region:            Rails.application.secrets.digitalocean_spaces_region,
  endpoint:          'https://nyc3.digitaloceanspaces.com',
  bucket:            Rails.application.secrets.digitalocean_spaces_bucket,
}

Shrine.storages = {
  # Shrine::Storage::S3.new(public: true, **s3_options)
  cache: Shrine::Storage::S3.new(public: true, prefix: "cache", upload_options: {acl: "public-read", content_disposition: ContentDisposition.attachment("Download name")}, **s3_options),
  store: Shrine::Storage::S3.new(public: true, prefix: "store", upload_options: {acl: "public-read", content_disposition: ContentDisposition.attachment("Download name")}, **s3_options),
}

# Check https://shrinerb.com/docs/plugins/activerecord for full list of plugins around ActiveRecord
# Check https://shrinerb.com/docs/external/extensions for full list of plugins/extensions
Shrine.plugin :activerecord
Shrine.plugin :cached_attachment_data # for retaining the cached file across form redisplays
Shrine.plugin :restore_cached_data # re-extract metadata when attaching a cached file
Shrine.plugin :uppy_s3_multipart # load the plugin
Shrine.plugin :backgrounding # adds background processing

Updated to upload_options to include ContentDisposition.attachment("Download name") for both cache and store. Still cones up at inline.

Note: All i'm trying to do is create a link that'll allow my users to download the file instead of just viewing it. So if there's another solution to this, i'm very much open to it.

masudhossain avatar Feb 18 '22 16:02 masudhossain

The code you posted looks like it should correctly set :content_disposition to attachment. Are you able to reproduce this behaviour with AWS S3? It's possible this is an issue with Digital Ocean Spaces.

janko avatar Feb 18 '22 16:02 janko

For me the last example works on AWS S3, so it seems it's an issue with DigitalOcean Spaces. Here is a self-contained example showing my recommended configuration:

require "shrine"
require "shrine/storage/s3"
require "content_disposition"
require "rack/test"
require "http"

s3_options = {
  access_key_id:     ENV.fetch("S3_ACCESS_KEY_ID"),
  secret_access_key: ENV.fetch("S3_SECRET_ACCESS_KEY"),
  region:            ENV.fetch("S3_REGION"),
  bucket:            ENV.fetch("S3_BUCKET"),
}

Shrine.storages = {
  cache: Shrine::Storage::S3.new(public: true, prefix: "cache", **s3_options),
  store: Shrine::Storage::S3.new(public: true, prefix: "store", **s3_options),
}

Shrine.plugin :presign_endpoint, presign_options: -> (request) do
  # Uppy will send the "filename" and "type" query parameters 
  filename = request.params["filename"]
  type     = request.params["type"]

  {
    content_disposition: ContentDisposition.attachment(filename), # download with original filename 
    content_type: type,                                # set correct content type 
  }
end

Shrine.plugin :upload_options, store: -> (io, options) do
  { content_disposition: ContentDisposition.attachment(io.original_filename) }
end

# imitate Uppy's presign request
presign_endpoint = Shrine.presign_endpoint(:cache)
session = Rack::Test::Session.new(presign_endpoint)
session.get("/?filename=foo.txt&type=text/plain")
data = JSON.parse(session.last_response.body)
# imitate Uppy's upload request
HTTP.post(data["url"], form: data["fields"].merge(file: HTTP::FormData::Part.new("foobar")))

cached_file = Shrine.uploaded_file(id: data["fields"]["key"].match("cache/").post_match, storage: :cache, metadata: { filename: "foo.txt" })

attacher = Shrine::Attacher.new
attacher.attach_cached(cached_file)
attacher.promote

object = attacher.store.storage.object(attacher.file.id)
object.head.content_disposition # => "attachment; filename=\"foo.txt\"; filename*=UTF-8''foo.txt"

janko avatar Oct 06 '22 21:10 janko