Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Download file from Amazon S3 using REST API

I have my own REST API to call in order to download a file. (At the end, the file could be store in different kind of server... Amazon s3, locally etc...)

To get a file from s3, I should use this method:

var url = s3.getSignedUrl('getObject', params);

This will give me a downloadable link to call.

Now, my question is, how can I use my own rest API to download a file when it comes from that link? Is there a way to redirect the call?

I'm using Hapi for my REST server.

{
    method: "GET", path: "/downloadFile",
    config: {auth: false},
    handler: function (request, reply) {
        // TODO
        reply({})
    }
},
like image 735
Jaythaking Avatar asked Feb 01 '17 16:02

Jaythaking


2 Answers

Instead of using a redirect to download the desired file, just return back an unbufferedStream instead from S3. An unbufferedStream can be returned from the HttpResponse within the AWS-SDK. This means there is no need to download the file from S3, then read it in, and then have the requester download the file.

FYI I use this getObject() approach with Express and have never used Hapi, however I think that I'm pretty close with the route definition but hopefully it will capture the essence of what I'm trying to achieve.

Hapi.js route

const getObject = require('./getObject');

{
    method: "GET", path: "/downloadFile",
    config: {auth: false},
    handler: function (request, reply) {
        let key = ''; // get key from request
        let bucket = ''; // get bucket from request

        return getObject(bucket, key)
          .then((response) => {
            reply.statusCode(response.statusCode);

            response.headers.forEach((header) => {
              reply.header(header, response.headers[header]);
            });

            return reply(response.readStream);
          })
          .catch((err) => {
            // handle err
            reply.statusCode(500);
            return reply('error');
          });
    }
},

getObject.js

const AWS = require('aws-sdk');
const S3 = new AWS.S3(<your-S3-config>);

module.exports = function getObject(bucket, key) {
  return new Promise((resolve, reject) => {
    // Get the file from the bucket
    S3.getObject({
      Bucket: bucket,
      Key: key
    })
      .on('error', (err) => {            
        return reject(err);
      })
      .on('httpHeaders', (statusCode, headers, response) => {
        // If the Key was found inside Bucket, prepare a response object
        if (statusCode === 200) {
          let responseObject = {
            statusCode: statusCode,
            headers: {
              'Content-Disposition': 'attachment; filename=' + key
            }
          };

          if (headers['content-type'])
            responseObject.headers['Content-Type'] = headers['content-type'];
          if (headers['content-length'])
            responseObject.headers['Content-Length'] = headers['content-length'];

          responseObject.readStream = response.httpResponse.createUnbufferedStream();
          return resolve(responseObject);
        }
      })
      .send();
  });
}
like image 112
peteb Avatar answered Nov 10 '22 19:11

peteb


Return a HTTP 303 Redirect with the Location header set to the blob's public URL in the S3 bucket.

If your bucket is private then you need to proxy the request instead of performing a redirect, unless your clients also have access to the bucket.

like image 30
Dai Avatar answered Nov 10 '22 19:11

Dai