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

时间:2011-10-22 15:04:41

标签: node.js file-upload amazon-s3

我正在创建一个应用程序,它需要一些文件上传并将它们直接发送到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();

现在我想在oposite方向上做类似的事情(从浏览器上传到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?

4 个答案:

答案 0 :(得分:20)

您要做的是覆盖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表示发送缓冲区已满。您应该暂停Formidable解析器。当您从Knox流中获得drain事件时,您应该恢复Formidable。

答案 1 :(得分:6)

请改用multiparty。它支持您想要的这种流式传输。它甚至有一个直接流式传输到s3的示例:https://github.com/superjoe30/node-multiparty/blob/master/examples/s3.js

答案 2 :(得分:0)

在Express中间件中,我将formidablePassThrough一起用于将文件流传输到S3(在我的情况下,是通过Minio SDK到与S3兼容的Minio;并且我相信它可以工作)同样的Minio SDK也适用于AWS S3)

这是示例代码。

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 GitHubits unit tests

答案 3 :(得分:-2)

您无法捕获流,因为数据必须由Formidable翻译。你给出的bufferbuffer.length块的文件内容:这可能是一个问题,因为查看Formidable的文档似乎在文件完全上传之前它无法可靠地报告文件大小和Knox的put方法可能需要它。

之前从未使用过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也有一个可能对您有用的写作。