aws-sdk
モジュールとExpress 4.13を使用すると、S3からさまざまな方法でファイルをプロキシできます。
このコールバックバージョンは、ファイル本体をバッファーとして、さらに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
});
}
このバージョンの問題は、ファイルを送信する前にファイル全体を取得する必要があることです。これは、特にビデオのような大きなファイルの場合、あまり良くありません。
このバージョンはファイルを直接ストリーミングします:
function(req,res){
var s3 = new AWS.S3();
s3.getObject({Bucket: myBucket, Key: myFile})
.createReadStream()
.pipe(res);
}
ただし、最初のヘッダーとは異なり、ブラウザーはファイルを適切に処理する必要があるかもしれないヘッダーについては何もしません。
S3から正しいヘッダーを通過させるが、ファイルをストリームとして送信するという、両方の長所を最大限に活用する方法はありますか?最初にHEAD
リクエストをS3に送信してメタデータを取得することで実行できますが、1回のAPI呼び出しで実行できますか?
1つのアプローチは、httpHeaders
イベントをリッスンし、その中にストリームを作成することです。
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();
私のプロジェクトでは、オブジェクトメタデータのみを取得するためにheadObjectを実行します(非常に高速で、オブジェクトのダウンロードを回避します)。次に、パイピングのために伝播する必要があるすべてのヘッダーを応答に追加します。
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);
});
AndréWerlangの答えに基づいて、AWS Request
オブジェクトをforwardToExpress
メソッドで拡張するために次のことを行いました。
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);
};
次に、ルートハンドラーで、次のようなことができます。
s3.getObject({Bucket: myBucket, Key: myFile}).forwardToExpress(res, next);