2016-03-03 1 views
13

Mit dem aws-sdk Modul und Express 4.13, ist es möglich, eine Datei von S3 eine Reihe von Möglichkeiten zu Proxy.Streaming-Datei von S3 mit Express einschließlich Informationen über Länge und Dateityp

wird diese Callback-Version der Datei Körper als Puffer sowie weitere relevante Header wie Content-Length zurück:

function(req,res){ 

    var s3 = new AWS.S3(); 

    s3.getObject({Bucket: myBucket, Key: myFile},function(err,data){ 

    if (err) { 
     return res.status(500).send("Error!"); 
    } 

    // Headers 
    res.set("Content-Length",data.ContentLength) 
     .set("Content-Type",data.ContentType); 

    res.send(data.Body); // data.Body is a buffer 

    }); 

} 

Das Problem bei dieser Version ist, dass Sie die gesamte Datei zu erhalten, bevor das Senden, das ist nicht großartig, vor allem wenn es sich um ein großes Video handelt.

Diese Version wird die Datei direkt streamen:

function(req,res){ 

    var s3 = new AWS.S3(); 

    s3.getObject({Bucket: myBucket, Key: myFile}) 
    .createReadStream() 
    .pipe(res); 

} 

Aber im Gegensatz zu den ersten, es wird nichts über die Header tun, die ein Browser richtig benötigen könnten, um die Datei zu bearbeiten.

Gibt es eine Möglichkeit, das Beste aus beiden Welten zu erhalten, indem Sie die richtigen Header von S3 übergeben, aber die Datei als Stream senden? Es könnte gemacht werden, indem zuerst eine HEAD Anfrage an S3 gestellt wird, um die Metadaten zu erhalten, aber kann es mit einem API-Aufruf gemacht werden?

Antwort

10

Für mein Projekt mache ich einfach ein HeadObject, um nur die Objektmetadaten zu erhalten (es ist wirklich schnell und vermeidet das Herunterladen des Objekts). Dann füge ich in der Antwort alle Header ich für die Rohrleitung propagieren müssen:

var s3 = new AWS.S3(); 

    var params = { 
     Bucket: bucket, 
     Key: key 
    }; 
    s3.headObject(params, function (err, data) { 
     if (err) { 
      // an error occurred 
      console.error(err); 
      return next(); 
     } 
     var stream = s3.getObject(params).createReadStream(); 

     // forward errors 
     stream.on('error', function error(err) { 
      //continue to the next middlewares 
      return next(); 
     }); 

     //Add the content type to the response (it's not propagated from the S3 SDK) 
     res.set('Content-Type', mime.lookup(key)); 
     res.set('Content-Length', data.ContentLength); 
     res.set('Last-Modified', data.LastModified); 
     res.set('ETag', data.ETag); 

     stream.on('end',() => { 
      console.log('Served by Amazon S3: ' + key); 
     }); 
     //Pipe the s3 object to the response 
     stream.pipe(res); 
    }); 
+0

Das ist ausgezeichnet. Vielen Dank! – matthoiland

+0

Sie bevorzugen also zwei HTTP-Anfragen statt einer? –

15

Ein Ansatz, die httpHeaders Ereignis zuhört und einen Strom in ihm zu schaffen.

s3.getObject(params) 
    .on('httpHeaders', function (statusCode, headers) { 
     res.set('Content-Length', headers['content-length']); 
     res.set('Content-Type', headers['content-type']); 
     this.response.httpResponse.createUnbufferedStream() 
      .pipe(res); 
    }) 
    .send(); 
4

Aufbauend auf André Werlang Antwort, wir haben die AWS zu erweitern folgende getan Request Objekte mit einer forwardToExpress Methode:

const _ = require('lodash'); 
const AWS = require('aws-sdk'); 

AWS.Request.prototype.forwardToExpress = function forwardToExpress(res, next) { 
    this 
    .on('httpHeaders', function (code, headers) { 
     if (code < 300) { 
      res.set(_.pick(headers, 'content-type', 'content-length', 'last-modified')); 
     }        
    }) 
    .createReadStream() 
    .on('error', next) 
    .pipe(res); 
};  

Dann in unserer Route Handler wir etwas tun können:

s3.getObject({Bucket: myBucket, Key: myFile}).forwardToExpress(res, next); 
+0

Das ist im Wesentlichen was wir machen. Ich würde vorschlagen, dass die Menge der Header, die Sie propagieren wollen (vorausgesetzt, Sie wollen Bereichsanfragen und eTag-basierte Client-Caching unterstützen) sind: ['accept-ranges', 'content-length', 'content-range', 'content -Typ ',' Etag ',' zuletzt geändert ']. – BobDickinson

Verwandte Themen