Abdullah Khawer
Abdullah Khawer

Reputation: 5678

S3 Presigned URL Multiple Content Disposition Headers

I have an S3 bucket having PDF files as objects and all of them are private. I create an S3 Presigned URL programmatically to get the object. It works fine. Now, I want it to be previewable as a PDF. Every object already has a Content-Type header set to application/pdf. Now, if I set the response-content-disposition header as a query parameter, it gets set but doesn't override the already existing Content-disposition header, instead, it creates a new one. If I set the Content-Disposition header in the metadata of the S3 object instead of adding it in the S3 Presigned URL as a query parameter, it still shows 2 headers. Is this some kind of bug on the AWS S3 side?

Below is the screenshot of the Response Header for reference.

Con

Any help will be much appreciated. Thanks.

Upvotes: 18

Views: 23176

Answers (2)

Abdullah Khawer
Abdullah Khawer

Reputation: 5678

I have solved this issue using the latest API available for this thing from AWS SDK for NodeJS using the following code:

const aws = require('aws-sdk');

const AWS_SIGNATURE_VERSION = 'v4';

const s3 = new aws.S3({
  accessKeyId: <aws-access-key>,
  secretAccessKey: <aws-secret-access-key>,
  region: <aws-region>,
  signatureVersion: AWS_SIGNATURE_VERSION
});

/**
 * Return a signed document URL given a Document instance
 * @param  {object} document Document
 * @return {string}          Pre-signed URL to document in S3 bucket
 */
const getS3SignedDocumentURL = (docName) => {
  const url = s3.getSignedUrl('getObject', {
    Bucket: <aws-s3-bucket-name>,
    Key: <aws-s3-object-key>,
    Expires: <url-expiry-time-in-seconds>,
    ResponseContentDisposition: `attachment; filename="${docName}"`
  });

  return url;
};

/**
 * Return a signed document URL previewable given a Document instance
 * @param  {object} document Document
 * @return {string}          Pre-signed URL to previewable document in S3 bucket
 */
const getS3SignedDocumentURLPreviewable = (docName) => {
  const url = s3.getSignedUrl('getObject', {
    Bucket: <aws-s3-bucket-name>,
    Key: <aws-s3-object-key>,
    Expires: <url-expiry-time-in-seconds>,
    ResponseContentDisposition: `inline; filename="${docName}"`
  });

  return url;
};

module.exports = {
  getS3SignedDocumentURL,
  getS3SignedDocumentURLPreviewable
};

Note: Don't forget to replace the placeholders (<...>) with actual values to make it work.

Upvotes: 32

Naresh Kumar
Naresh Kumar

Reputation: 1736

It's strange how often we overlook things like filenames where comma (,) could be common if it is user generated name. While setting response-content-disposition make sure to strip special character or properly escape the filename attribute

Refer https://stackoverflow.com/a/6745788/8813684 for more details

Upvotes: 3

Related Questions