[英]Stream uploading file to S3 on Node.js using formidable and (knox or aws-sdk)
我正在尝试使用aws-sdk或knox将通过表单提交的文件直接上传到Amazon S3存储桶。 表单处理非常强大 。
我的问题是:如何使用这些库的最新功能处理流,将aws-sdk(或knox)与可怕的功能一起使用?
我知道这里已经以不同的方式询问了这个主题,即:
但是,我认为答案有点过时和/或离题(例如,出于各种原因,我现在不希望使用CORS支持),和/或最重要的是,请勿参考的最新功能。要么是aws-sdk(请参阅: https : //github.com/aws/aws-sdk-js/issues/13#issuecomment-16085442 )或knox(尤其是putStream()或它的可读Stream.pipe(req)变体, 两者都解释了在文档中 )。
经过数小时的奋斗,我得出的结论是我需要一些帮助(免责声明:我是使用流技术的新手)。
HTML形式:
<form action="/uploadPicture" method="post" enctype="multipart/form-data">
<input name="picture" type="file" accept="image/*">
<input type="submit">
</form>
Express bodyParser中间件是通过以下方式配置的:
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:在超时时间内未读取或写入您与服务器的套接字连接。 空闲连接将关闭。]
消息:'在超时时间内未读取或写入您与服务器的套接字连接。 空闲连接将关闭。”,代码:“ RequestTimeout”,名称:“ RequestTimeout”,statusCode:400,可重试: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
)
我还在某个地方得到了一个带有400 statusCode的大型res对象。
在两种情况下,Region都配置为eu-west-1 。
补充笔记:
节点0.10.12
npm(1.0.14)的最新强大功能
来自npm(1.3.1)的最新aws-sdk
来自npm的最新诺克斯(0.8.3)
好吧, 据Formidable的创建者说,不可能直接流到Amazon S3:
S3 API要求您在创建新文件时提供它们的大小。 在完全接收多部分/表单数据文件之前,此信息不可用。 这意味着流是不可能的。
实际上, form.bytesExpected是指整个表单的大小,而不是单个文件的大小。
因此,在将数据上传到S3之前,必须先将其击中内存或服务器上的磁盘。
使用AWS S3的multipartUpload( s3-upload-stream作为工作模块)和node-formidable的可读流 ,您可以通过管道将其上传,如下所示 :
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);
由于这篇文章太老了,我相信现在支持直接流式传输,因此我花了很多时间阅读有关该主题的过时答案...
如果它可以帮助任何人,而我又可以直接从客户端将其流式传输到s3,而无需安装软件包:
https://gist.github.com/mattlockyer/532291b6194f6d9ca40cb82564db9d2a
服务器假定req
是一个流对象,在我的例子中,在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的帮助。
尝试将“ ContentType”添加到Upload参数( https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#upload-property )
...
const params = {
Key,
Bucket: bucketName,
Body: req,
ContentType: 'image/jpg'
};
s3.upload(params, (err, data) => {
if (err) return err;
console.log(data);
});
...
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.