Skip to content

Instantly share code, notes, and snippets.

@diamondap
Last active July 5, 2021 19:52
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save diamondap/9134867 to your computer and use it in GitHub Desktop.
Save diamondap/9134867 to your computer and use it in GitHub Desktop.
Overriding S3 HTTP Headers (Ruby)
# Given an S3 URL, this returns a signed S3 url that will force the browser
# to download the file rather than opening it in the browser window. The key
# is to add response-content-disposition to the query string, which tells S3
# to send the content-disposition header you specify. S3 will respect this
# parameter only if the URL is signed. You can override other S3 headers such
# as content-type using this same method. See:
#
# http://docs.aws.amazon.com/AmazonS3/latest/API/RESTObjectGET.html
#
def s3_download_url(url)
s3_key = URI(url).path
filename = s3_key.split('/')[-1]
disposition = "response-content-disposition=attachment;filename=#{filename}"
expires = 60.minutes.from_now.utc.to_i
query_string_char = url.include?('?') ? '&' : '?'
string_to_sign = "GET\n\n\n#{expires}\n#{s3_key}#{query_string_char}#{disposition}"
digest = OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'),
S3FileField.config.secret_access_key,
string_to_sign)
signature = Base64.encode64(digest).gsub("\n", "").gsub('+', '%2B')
"#{url}#{query_string_char}#{disposition}&AWSAccessKeyId=#{AWS.config.access_key_id}&Expires=#{expires}&Signature=#{signature}"
end
@laertiades
Copy link

Thank you for this. I had to make the following modifications to get it to work for me:

  def S3DownloadHelper.s3_download_url(url)
    s3_key = URI(url).path
    filename = s3_key.split('/')[-1]
    disposition = "response-content-disposition=attachment;filename=#{filename.gsub('%20', ' ')}"
    expires = 60.minutes.from_now.utc.to_i
    query_string_char = url.include?('?') ? '&' : '?'
    string_to_sign = ["GET", "", "", "#{expires}", "/#{ENV['S3_BUCKET_NAME']}#{s3_key.gsub(' ', '%20')}#{query_string_char}#{disposition}"].join("\n")
    digest = OpenSSL::HMAC.digest('sha1', ENV['AWS_SECRET_ACCESS_KEY'], string_to_sign)
    signature = Base64.strict_encode64(digest).gsub("\n", "").gsub('+', '%2B')
    "#{url}#{query_string_char}#{disposition}&AWSAccessKeyId=#{ENV['AWS_ACCESS_KEY_ID']}&Expires=#{expires}&Signature=#{signature}"
  end

@causztic
Copy link

causztic commented Jan 19, 2017

If you use the aws-sdk, you can do this:

client = Aws::S3::Client.new(credentials: Aws::Credentials.new(access_key, secret))
signer = Aws::S3::Presigner.new(client: client)
signer.presigned_url(:get_object, bucket: bucket, key: key, response_content_disposition: 'attachment')

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment