Make S3 serve attachment using the content disposition header

pull/7456/head
Markus Kahl 5 years ago
parent 3240d3b24a
commit 6f287a512a
  1. 2
      app/models/attachment.rb
  2. 2
      app/uploaders/file_uploader.rb
  3. 16
      app/uploaders/fog_file_uploader.rb
  4. BIN
      spec/fixtures/files/textfile.txt.gz
  5. 55
      spec/models/attachment_spec.rb

@ -60,7 +60,7 @@ class Attachment < ActiveRecord::Base
# Returns an URL if the attachment is stored in an external (fog) attachment storage
# or nil otherwise.
def external_url
url = URI.parse file.download_url # returns a path if local
url = URI.parse file.download_url(content_disposition: content_disposition) # returns a path if local
url if url.host
rescue URI::InvalidURIError

@ -49,7 +49,7 @@ module FileUploader
file.to_file
end
def download_url
def download_url(options = {})
file.is_path? ? file.path : file.url
end

@ -57,8 +57,20 @@ class FogFileUploader < CarrierWave::Uploader::Base
super
end
def download_url
remote_file.url
def download_url(options = {})
url_options = {}
if options[:content_disposition].present?
url_options[:query] = {
# Passing this option to S3 will make it serve the file with the
# respective content disposition. Without it no content disposition
# header is sent. This only works for S3 but we don't support
# anything else anyway (see carrierwave.rb).
"response-content-disposition" => options[:content_disposition]
}
end
remote_file.url url_options
end
##

Binary file not shown.

@ -233,4 +233,59 @@ describe Attachment, type: :model do
expect(File.exists?(attachment.file.path)).to eq false
end
end
describe "#external_url" do
let(:author) { FactoryBot.create :user }
let(:image_path) { Rails.root.join("spec/fixtures/files/image.png") }
let(:text_path) { Rails.root.join("spec/fixtures/files/testfile.txt") }
let(:binary_path) { Rails.root.join("spec/fixtures/files/textfile.txt.gz") }
let(:image_attachment) { Attachment.new author: author, file: File.open(image_path) }
let(:text_attachment) { Attachment.new author: author, file: File.open(text_path) }
let(:binary_attachment) { Attachment.new author: author, file: File.open(binary_path) }
before do
Fog.mock!
connection = Fog::Storage.new provider: "AWS"
connection.directories.create key: "my-bucket"
CarrierWave::Configuration.configure_fog! credentials: {}, directory: "my-bucket", public: false
# Remounting the uploader overrides the original file setter taking care of setting,
# among other things, the content type. So we have to restore that original
# method this way.
Attachment.alias_method :set_file, :file=
Attachment.mount_uploader :file, FogFileUploader
Attachment.alias_method :file=, :set_file
end
describe "for an image file" do
before { image_attachment.save! }
it "should make S3 use content_disposition inline" do
expect(image_attachment.content_disposition).to eq "inline"
expect(image_attachment.external_url.to_s).to include "response-content-disposition=inline"
end
end
describe "for a text file" do
before { text_attachment.save! }
it "should make S3 use content_disposition inline" do
expect(text_attachment.content_disposition).to eq "inline"
expect(text_attachment.external_url.to_s).to include "response-content-disposition=inline"
end
end
describe "for a binary file" do
before { binary_attachment.save! }
it "should make S3 use content_disposition 'attachment; filename=...'" do
expect(binary_attachment.content_disposition).to eq "attachment; filename=textfile.txt.gz"
expect(binary_attachment.external_url.to_s).to include "response-content-disposition=attachment%3B%20filename%3Dtextfile.txt.gz"
end
end
end
end

Loading…
Cancel
Save