【问题标题】: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?

【问题讨论】:

    标签: node.js file-upload amazon-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 也有一篇文章可能对您有用。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-12-25
          • 2022-10-07
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-04-27
          相关资源
          最近更新 更多