从节点强大的文件上传访问原始文件流
Posted
技术标签:
【中文标题】从节点强大的文件上传访问原始文件流【英文标题】:Accessing the raw file stream from a node-formidable file upload 【发布时间】:2011-12-13 05:04:58 【问题描述】:我正在创建一个应用程序,它需要上传一些文件并将它们直接发送到 S3。我什至不想在我的服务器上拥有 tmp 文件,所以我使用 Knox 模块并希望从 Formidable 获取原始流并通过 Knox 将其发送到 S3。我使用 Knox 做了类似的事情来使用此代码下载文件:
knox.downloads.get(widget.download).on('response',function(sres)
res.writeHead(200,
'Content-Type':'application/zip',
'Content-Length': sres.headers['content-length'],
'Content-Disposition':'attachment; filename=' + widget.download
);
util.pump(sres, res);
).end();
现在我想在相反的方向做一些类似的事情(文件从浏览器上传到 S3)。
到目前为止,我已经编写了一个事件处理程序来捕获文件上传时的每条数据:
var form = new formidable.IncomingForm();
form.onPart = function(part)
if(!part.filename)
form.handlePart(part);
else
if(part.name == 'download')
// Upload to download bucket
controller.putDownload(part);
else
// Upload to the image bucket
controller.putImage(part);
//res.send(sys.inspect(part));
form.parse(req, function(err, fields, files)
if(err)
res.json(err);
else
res.send(sys.inspect(fields:fields, files:files), 'content-type':'text/plain');
//controller.createWidget(res,fields,files);
);
controller.putDownload = function(part)
part.addListener('data', function(buffer)
knox.download.putStream(data,part.filename, function(err,s3res)
if(err)throwError(err);
else
console.log(s3res);
);
)
knox.downloads.putStream(part, part.filename, function(err,s3res)
if(err)throwError(err);
else
console.log(s3res);
);
但数据事件只给了我缓冲区。那么是否可以捕获流本身并将其推送到 S3?
【问题讨论】:
【参考方案1】:您要做的是覆盖Form.onPart
方法:
IncomingForm.prototype.onPart = function(part)
// this method can be overwritten by the user
this.handlePart(part);
;
Formidable 的默认行为是将部件写入文件。你不想要那个。您想要处理“部分”事件以写入 knox 下载。从这个开始:
form.onPart = function(part)
if (!part.filename)
// let formidable handle all non-file parts
form.handlePart(part);
return;
然后打开 knox 请求并自己处理原始部分事件:
part.on('data', function(data)
req.write(data);
);
part.on('end', function()
req.end();
);
part.on('error', function(err)
// handle this too
);
作为奖励,如果req.write(data)
返回 false,则表示发送缓冲区已满。您应该暂停强大的解析器。当您从 Knox 流中收到 drain
事件时,您应该恢复 Formidable。
【讨论】:
【参考方案2】:请改用multiparty。它支持您想要的这种流媒体。它甚至有一个直接流式传输到 s3 的示例:https://github.com/superjoe30/node-multiparty/blob/master/examples/s3.js
【讨论】:
我被卡住了,切换到多方解决了这个问题【参考方案3】:在 Express 中间件中,我使用 formidable
和 PassThrough
将文件流式上传到 S3(在我的情况下,上传到通过 Minio SDK 与 S3 兼容的 Minio;我相信它也适用于 AWS S3使用相同的 Minio SDK)
这里是示例代码。
const formidable = require('formidable')
const PassThrough = require('stream')
const form = new formidable.IncomingForm()
const pass = new PassThrough()
const fileMeta =
form.onPart = part =>
if (!part.filename)
form.handlePart(part)
return
fileMeta.name = part.filename
fileMeta.type = part.mime
part.on('data', function (buffer)
pass.write(buffer)
)
part.on('end', function ()
pass.end()
)
form.parse(req, err =>
if (err)
req.minio = error: err
next()
else
handlePostStream(req, next, fileMeta, pass)
)
而handlePostStream
如下所示,供您参考:
const uuidv1 = require('uuid/v1')
const handlePostStream = async (req, next, fileMeta, fileStream) =>
let filename = uuidv1()
try
const metaData =
'content-type': fileMeta.type,
'file-name': Buffer.from(fileMeta.name).toString('base64')
const minioClient = /* Get Minio Client*/
await minioClient.putObject(MINIO_BUCKET, filename, fileStream, metaData)
req.minio = post: filename: `$filename`
catch (error)
req.minio = error
next()
你可以找到the source code on GitHub,也可以找到its unit tests。
【讨论】:
【参考方案4】:您无法捕获流,因为数据必须由 Formidable 翻译。你得到的buffer
是buffer.length
块中的文件内容:这可能是一个问题,因为查看 Formidable 的文档似乎在文件完全上传之前它无法可靠地报告文件大小和 Knox 的@ 987654324@ 方法可能需要那个。
以前从未以这种方式使用过 Knox,但您可能会遇到这样的事情:
controller.putDownload = function(part)
var req = knox.download.put(part.filename,
'Content-Type': 'text/plain'
);
part.addListener('data', function(buffer)
req.write(buffer);
);
req.on('response', function(res)
// error checking
);
req.end();
有点不确定响应检查位,但是....看看你能不能把它变成形状。此外,Streaming an octet stream from request to S3 with knox on node.js 也有一篇文章可能对您有用。
【讨论】:
以上是关于从节点强大的文件上传访问原始文件流的主要内容,如果未能解决你的问题,请参考以下文章