Search code examples
node.jsamazon-web-servicesamazon-s3content-dispositionpre-signed-url

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.


Solution

  • 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.