2016-03-03 15 views
13

Korzystając z modułu aws-sdk i Express 4.13, możliwe jest proxy pliku z S3 na kilka sposobów.Przesyłanie strumieniowe pliku z S3 z Expressem, w tym informacje o długości i rodzaju pliku

Ta wersja zwrotna powróci ciało pliku jako bufor, a także innych właściwych nagłówków jak Content-Length:

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 

    }); 

} 

Problem z tej wersji jest to, że trzeba się cały plik przed wysłaniem, co jest nie świetne, zwłaszcza jeśli jest to coś dużego jak wideo.

Wersja ta będzie bezpośrednio przesyłać plik:

function(req,res){ 

    var s3 = new AWS.S3(); 

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

} 

Ale w przeciwieństwie do pierwszego, to nie będzie nic o nagłówkach, które mogą potrzebować, aby prawidłowo obsłużyć plik przeglądarka zrobić.

Czy istnieje sposób, aby uzyskać najlepsze z obu światów, przechodząc przez odpowiednie nagłówki z S3, ale wysyłając plik jako strumień? Można to zrobić, najpierw wysyłając żądanie S3, aby uzyskać metadane, ale czy można to zrobić za pomocą jednego wywołania API?

Odpowiedz

10

Dla mojego projektu, po prostu wykonuję headObject w celu pobrania tylko metadanych obiektu (jest naprawdę szybki i unika się pobrania obiektu). Następnie dodaję w odpowiedzi wszystkie nagłówki muszę propagować dla rurociągów:

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

To jest doskonałe. Dzięki! – matthoiland

+0

Więc wolisz dwa żądania HTTP zamiast jednego? –

15

Jedno podejście nasłuchuje zdarzenia httpHeaders i tworząc strumień wewnątrz niej.

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

Opierając się na odpowiedź André Werlang za, zrobiliśmy następujące poszerzyć AWS Request obiektów z forwardToExpress metody:

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); 
};  

Następnie w naszych obsługą tras, możemy zrobić coś takiego:

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

Jest to zasadniczo to, co robimy. Sugerowałbym, że zestaw nagłówków, które chcesz propagować (zakładając, że chcesz obsługiwać żądania zakresu i buforowanie klienta oparte na eTag) to prawdopodobnie: ['accept-ranges', 'content-length', 'content-range', 'content -type ',' etag ',' last-modified ']. – BobDickinson

Powiązane problemy