使用强大的和(knox或aws-sdk)在Node.js上传文件到S3

我正尝试使用aws-sdk或knox将通过表单提交的文件直接上传到Amazon S3存储桶。 表格处理是强大的 。

我的问题是:如何正确使用aws-sdk(或knox)强大的处理stream使用这些库的最新function?

我知道这个话题已经在这里以不同的口味被提出来了,

  • 如何使用node.js强大的库来接收上传的文件并使用knox将其保存到Amazon S3?
  • 节点应用程序stream文件直接上传到亚马逊S3
  • 从一个节点强大的file upload (以及它在overiding form.onPart()上非常有用的接受的答案) 访问原始文件stream

不过,我相信答案有点过时和/或偏离主题(即CORS的支持,目前我不想使用它,因为各种原因)和/或最重要的是,没有提到最新的function或者aws-sdk(参见: https : //github.com/aws/aws-sdk-js/issues/13#issuecomment-16085442 )或者knox(特别是putStream()或者其可读的stream标记(req)变体) 在文档中 )。

经过几个小时的挣扎之后,我得出结论,我需要一些帮助(免责声明:我是一个相当新鲜的stream)。

HTML表单:

<form action="/uploadPicture" method="post" enctype="multipart/form-data"> <input name="picture" type="file" accept="image/*"> <input type="submit"> </form> 

Express bodyParser中间件是这样configuration的:

 app.use(express.bodyParser({defer: true})) 

POST请求处理程序:

 uploadPicture = (req, res, next) -> form = new formidable.IncomingForm() form.parse(req) form.onPart = (part) -> if not part.filename # Let formidable handle all non-file parts (fields) form.handlePart(part) else handlePart(part, form.bytesExpected) handlePart = (part, fileSize) -> # aws-sdk version params = Bucket: "mybucket" Key: part.filename ContentLength: fileSize Body: part # passing stream object as body parameter awsS3client.putObject(params, (err, data) -> if err console.log err else console.log data ) 

但是,我收到以下错误:

{[RequestTimeout:您的套接字到服务器的连接在超时期限内没有被读取或写入。 空闲连接将被closures。]

消息:“您的套接字连接到服务器没有读取或写入在超时期限内。 ',code:'RequestTimeout',名称:'RequestTimeout',statusCode:400,retryable:false}

一个knox版本的handlePart()函数也是这种方式定制的,可悲的是失败了:

 handlePart = (part, fileSize) -> headers = "Content-Length": fileSize "Content-Type": part.mime knoxS3client.putStream(part, part.filename, headers, (err, res) -> if err console.log err else console.log res ) 

我也得到一个大的res对象与400 statusCode某处。

在这两种情况下,Region都被configuration为欧洲西部1

补充笔记:

节点0.10.12

最新从npm(1.0.14)

npm(1.3.1)的最新aws-sdk

来自npm的最新knox(0.8.3)

那么根据Formidable的创造者 ,直接stream向Amazon S3是不可能的:

S3 API需要您在创build新文件时提供大小。 这些信息在完全收到之前不可用于多部分/表单数据文件。 这意味着stream是不可能的。

的确, form.bytesExpected是指整个表单的大小,而不是单个文件的大小。

因此,数据必须先上传到服务器上的内存或磁盘上。

使用AWS S3的multipartUpload( s3-upload-stream作为工作模块)和node-formidable的可读stream ,您可以将stream传输到上传,如下所示 :

 var formidable = require('formidable'); var http = require('http'); var util = require('util'); var AWS = require('aws-sdk'); var config = require('./config'); var s3 = new AWS.S3({ accessKeyId: config.get('S3_ACCESS_KEY'), secretAccessKey: config.get('S3_SECRET_KEY'), apiVersion: '2006-03-01' }); var s3Stream = require('s3-upload-stream')(s3); var bucket = 'bucket-name'; var key = 'abcdefgh'; http.createServer(function(req, res) { if (req.url == '/upload' && req.method.toLowerCase() == 'post') { var form = new formidable.IncomingForm(); form.on('progress', function(bytesReceived, bytesExpected) { //console.log('onprogress', parseInt( 100 * bytesReceived / bytesExpected ), '%'); }); form.on('error', function(err) { console.log('err',err); }); // This 'end' is for the client to finish uploading // upload.on('uploaded') is when the uploading is // done on AWS S3 form.on('end', function() { console.log('ended!!!!', arguments); }); form.on('aborted', function() { console.log('aborted', arguments); }); form.onPart = function(part) { console.log('part',part); // part looks like this // { // readable: true, // headers: // { // 'content-disposition': 'form-data; name="upload"; filename="00video38.mp4"', // 'content-type': 'video/mp4' // }, // name: 'upload', // filename: '00video38.mp4', // mime: 'video/mp4', // transferEncoding: 'binary', // transferBuffer: '' // } var start = new Date().getTime(); var upload = s3Stream.upload({ "Bucket": bucket, "Key": part.filename }); // Optional configuration //upload.maxPartSize(20971520); // 20 MB upload.concurrentParts(5); // Handle errors. upload.on('error', function (error) { console.log('errr',error); }); upload.on('part', function (details) { console.log('part',details); }); upload.on('uploaded', function (details) { var end = new Date().getTime(); console.log('it took',end-start); console.log('uploaded',details); }); // Maybe you could add compress like // part.pipe(compress).pipe(upload) part.pipe(upload); }; form.parse(req, function(err, fields, files) { res.writeHead(200, {'content-type': 'text/plain'}); res.write('received upload:\n\n'); res.end(util.inspect({fields: fields, files: files})); }); return; } // show a file upload form res.writeHead(200, {'content-type': 'text/html'}); res.end( '<form action="/upload" enctype="multipart/form-data" method="post">'+ '<input type="text" name="title"><br>'+ '<input type="file" name="upload" multiple="multiple"><br>'+ '<input type="submit" value="Upload">'+ '</form>' ); }).listen(8080); 

由于这篇文章太老了,我相信现在支持直接stream式传输,所以我花了很多时间阅读关于这个主题的过时的答案。

如果能够帮助任何人,无需安装软件包,就可以直接从客户端stream向s3。

https://gist.github.com/mattlockyer/532291b6194f6d9ca40cb82564db9d2a

服务器假定req是一个stream对象,在我的情况下,在xhr(send)中使用一个File对象,它将在现代浏览器中发送二进制数据。

 const fileUploadStream = (req, res) => { //get "body" args from header const { id, fn } = JSON.parse(req.get('body')); const Key = id + '/' + fn; //upload to s3 folder "id" with filename === fn const params = { Key, Bucket: bucketName, //set somewhere Body: req, //req is a stream }; s3.upload(params, (err, data) => { if (err) { res.send('Error Uploading Data: ' + JSON.stringify(err) + '\n' + JSON.stringify(err.stack)); } else { res.send(Key); } }); }; 

是的,它打破了惯例,但如果你看看这个要点,它比我发现依赖于其他软件包的任何东西都要干净得多。

+1为实用主义和感谢@SalehenRahman的帮助。