从节点强大的文件上传访问原始文件流

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 中间件中,我使用 formidablePassThrough 将文件流式上传到 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 翻译。你得到的bufferbuffer.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 也有一篇文章可能对您有用。

【讨论】:

以上是关于从节点强大的文件上传访问原始文件流的主要内容,如果未能解决你的问题,请参考以下文章

javascript结合nodejs实现多文件上传

文件上传 servlet 从HttpServletRequest.getInputStream()中获得消息内容

文件上传

节点强大的创建大小为零字节的不必要文件

测试扩展——javaweb文件上传逻辑

从request中获取文件流的两种方式,配置文件上传大小