【问题标题】:Stream uploaded file to Azure blob storage with Node使用 Node 将上传的文件流式传输到 Azure Blob 存储
【发布时间】:2013-08-21 11:48:38
【问题描述】:

将 Express 与 Node 结合使用,我可以成功上传文件并将其通过以下代码块传递到 Azure 存储。

app.get('/upload', function (req, res) {
    res.send(
    '<form action="/upload" method="post" enctype="multipart/form-data">' +
    '<input type="file" name="snapshot" />' +
    '<input type="submit" value="Upload" />' +
    '</form>'
    );
});

app.post('/upload', function (req, res) {
    var path = req.files.snapshot.path;
    var bs= azure.createBlobService();
    bs.createBlockBlobFromFile('c', 'test.png', path, function (error) { });
    res.send("OK");
});

这很好用,但 Express 创建了一个临时文件并首先存储图像,然后我从文件将其上传到 Azure。在这个过程中,这似乎是一个低效且不必要的步骤,我最终不得不管理临时文件目录的清理。

我应该能够使用 Azure SDK 中的 blobService.createBlockBlobFromStream 方法将文件直接流式传输到 Azure 存储,但我对 Node 或 Express 不够熟悉,无法理解如何访问流数据。

app.post('/upload', function (req, res) {

    var stream = /// WHAT GOES HERE ?? ///

    var bs= azure.createBlobService();
    bs.createBlockBlobFromStream('c', 'test.png', stream, function (error) { });
    res.send("OK");
});

我发现以下博客表明可能有一种方法可以做到这一点,当然 Express 正在抓取流数据并解析并将其保存到文件系统中。 http://blog.valeryjacobs.com/index.php/streaming-media-from-url-to-blob-storage/

vjacobs 代码实际上是从另一个站点下载文件并将该流传递到 Azure,所以我不确定它是否可以适应我的情况。

如何使用 Node 访问上传的文件流并将其直接传递到 Azure?

【问题讨论】:

  • 我不熟悉节点,但Github page 可能会提供提示。 var stream = fs.createReadStream(req.files.snapshot.path); 其中fs 定义为var fs = require('fs');
  • @Romoku 不幸的是,req.files.snapshot.path 是磁盘上文件的路径。我需要在它变成实际文件之前捕获它。
  • 从我读到的bodyParser 中间件处理将文件写入磁盘。据我所知,您需要实现自己的中间件,以便在文件写入磁盘之前拦截文件。
  • 还要考虑不将文件写入磁盘的含义。您将失去持久性并增加应用程序内存占用。

标签: node.js azure express azure-blob-storage


【解决方案1】:

解决方案(基于与@danielepolencic 的讨论)

使用 Multiparty(npm install multiparty),一个 Formidable 的分支,如果我们禁用 Express 的 bodyparser() 中间件,我们可以访问多部分数据(有关更多信息,请参阅他们的注释)。与 Formidable 不同,Multiparty 不会将文件流式传输到磁盘,除非您告诉它这样做。

app.post('/upload', function (req, res) {
    var blobService = azure.createBlobService();
    var form = new multiparty.Form();
    form.on('part', function(part) {
        if (part.filename) {

            var size = part.byteCount - part.byteOffset;
            var name = part.filename;

            blobService.createBlockBlobFromStream('c', name, part, size, function(error) {
                if (error) {
                    res.send({ Grrr: error });
                }
            });
        } else {
            form.handlePart(part);
        }
    });
    form.parse(req);
    res.send('OK');
});

感谢@danielepolencic 帮助找到解决方案。

【讨论】:

  • 请注意,根据 Multiparty 在 github 上的 repo,github.com/andrewrk/node-multiparty,他们声称 busboy 是一种更新、更快的替代方案,可能值得研究:github.com/mscdex/busboy
  • 基准测试声称 busboy(前身为 dicer)的速度是多方的两倍多,而且非常强大:github.com/mscdex/dicer/wiki/Benchmarks
  • 请注意:使用此解决方案,我发现图像的尾部未正确上传。解决方案是从大小中减去 byteOffset。
  • @SoroushKhanlou 很有可能,因为这个答案已经有将近 2 年的历史了。
  • 有没有关于如何用 busboy 实现相同的示例代码?不幸的是,我无法让它工作:(
【解决方案2】:

正如您从connect middleware documentation 中看到的那样,bodyparser 会自动为您处理表单。在您的特定情况下,它会解析传入的多部分数据并将其存储在其他地方,然后以一种很好的格式(即req.files)公开保存的文件。

不幸的是,我们不需要(也不需要类似)黑魔法,主要是因为我们希望能够将传入的数据直接流式传输到 azure 而不会碰到磁盘(即req.pipe(res))。因此,我们可以关闭bodyparser中间件,自己处理传入的请求。在底层,bodyparser 使用 node-formidable,因此在我们的实现中重用它可能是个好主意。

var express = require('express');
var formidable = require('formidable');
var app = express();

// app.use(express.bodyParser({ uploadDir: 'temp' }));

app.get('/', function(req, res){
  res.send('hello world');
});

app.get('/upload', function (req, res) {
    res.send(
    '<form action="/upload" method="post" enctype="multipart/form-data">' +
    '<input type="file" name="snapshot" />' +
    '<input type="submit" value="Upload" />' +
    '</form>'
    );
});

app.post('/upload', function (req, res) {
  var bs = azure.createBlobService();
  var form = new formidable.IncomingForm();
  form.onPart = function(part){
    bs.createBlockBlobFromStream('taskcontainer', 'task1', part, 11, function(error){
      if(!error){
          // Blob uploaded
      }
    });
  };
  form.parse(req);
  res.send('OK');
});

app.listen(3000);

核心思想是我们可以利用node streams,这样我们就不需要在内存中加载完整的文件就可以将它发送到azure,但我们可以在它出现时传输它。强大的节点模块支持流,因此将流通过管道传输到 azure 将实现我们的目标。

您可以通过将post 路由替换为:

app.post('/upload', function (req, res) {
  var form = new formidable.IncomingForm();
    form.onPart = function(part){
      part.pipe(res);
    };
    form.parse(req);
});

在这里,我们只是将请求从输入传送到输出。你可以阅读更多关于bodyParserhere的信息。

【讨论】:

  • 谢谢,这更接近最终解决方案,但在bs.createBlockBlobFromFile 的回调中包含 form.onPart 在这里无济于事。 createBlockBlobFromFile 需要一个文件路径,此时该路径不存在。我需要以某种方式将form.onPartcreateBlockBlobFromStream 结合起来,后者采用ReadableStream 对象。
  • 我根据azure sdk for node更新了sn-p。但更简单地说,part (also) 是一个可读流(即它提供输出),因此等同于 fs.createReadStream
  • 这越来越近了。我们需要用正确的流大小替换11。给它form.expectedBytes 是不正确的,我也尝试在代码中指定正确的字节,但这也不起作用。 createBlobFromBlockStream 在超时之前永远不会返回。
  • 暂且不提,我们以后应该把你的留给别人,我会发布解决方案作为答案,但奖励你。
  • 谢谢。顺便说一句,非常有趣的问题/解决方案。
【解决方案3】:

通过 Azure Storage SDK for Node 上传二进制数据(例如图像)有不同的选项,而不是使用多部分。

基于 Node 中的 Buffer 和 Stream 定义并对其进行操作,可以使用几乎所有的 BLOB 上传方法来处理这些问题:createWriteStreamToBlockBlobcreateBlockBlobFromStreamcreateBlockBlobFromText

可以在此处找到参考:Upload a binary data from request body to Azure BLOB storage in Node.js [restify]

【讨论】:

  • 有没有不使用restify的github等价物?
【解决方案4】:

在尝试实施解决方案时遇到 .createBlockBlobFromStream 问题的人,请注意,此方法在较新版本中略有更改

旧版本:

createBlockBlobFromStream(containerName, blobName, part, size, callback)

新版本

createBlockBlobFromStream(containerName, blobName, part, size, options, callback)

(如果您不关心选项,请尝试使用空数组)作为参数。

奇怪的是,“选项”应该是可选的,但无论出于何种原因,如果我忽略它,我的会失败。

【讨论】:

    猜你喜欢
    • 2021-01-10
    • 1970-01-01
    • 1970-01-01
    • 2019-05-26
    • 2019-10-14
    • 2017-01-24
    • 2017-08-19
    • 2019-01-27
    • 2020-01-03
    相关资源
    最近更新 更多