Загрузите загруженный файл в хранилище Azure blob с помощью Node

Используя 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 с помощью метода blobService.createBlockBlobFromStream в Azure SDK, но я недостаточно хорошо знаком с 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, поэтому я не уверен, что он может быть адаптирован для работы в моей ситуации.

Как я могу получить доступ и передать поток загруженных файлов непосредственно на Azure с помощью Node?

Ответ 1

РЕШЕНИЕ (на основе обсуждения с @danielepolencic)

Используя Multiparty (npm install multiparty), fork of Formidable, мы можем получить доступ к многочастным данным, если мы отключим промежуточное ПО bodyparser() из Express (подробнее см. их заметки об этом). В отличие от Formableable, 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 для того, чтобы помочь найти решение этого.

Ответ 2

Как вы можете прочитать подключить документацию промежуточного программного обеспечения, bodyparser автоматически обрабатывает форму для вас. В вашем конкретном случае он анализирует входящие многостраничные данные и сохраняет их где-то в другом месте, а затем предоставляет сохраненный файл в хорошем формате (т.е. req.files).

К сожалению, мы не нуждаемся (и необходимо как) черную магию прежде всего потому, что хотим иметь возможность напрямую передавать входящие данные в лазурь без попадания на диск (т.е. 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 потоки, чтобы нам не нужно было загружать в память полный файл до мы можем отправить его на лазурь, но мы можем перенести его по мере его поступления. node -формируемый модуль поддерживает потоки, следовательно, поток потока в лазурь достигнет нашей цели.

Вы можете легко протестировать код локально, не нажимая лазурь, заменив маршрут post на:

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

Здесь мы просто прокладываем запрос с входа на вывод. Вы можете узнать больше о bodyparser здесь.

Ответ 3

Существуют различные варианты загрузки двоичных данных (например, изображений) через Azure Storage SDK для Node, а не для использования multipart.

Основываясь на определениях буфера и потока в Node и манипулируя ими, их можно обрабатывать, используя почти все методы загрузки BLOB: createWriteStreamToBlockBlob, createBlockBlobFromStream, createBlockBlobFromText.

Ссылки можно найти здесь: Загрузить двоичные данные из тела запроса в хранилище BLUE в Azure в Node.js [восстановить]

Ответ 4

У людей, имеющих проблемы с .createBlockBlobFromStream, пытающимися реализовать решения, обратите внимание, что этот метод несколько изменился в более новых версиях

Старая версия:

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

Новая версия

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

(если вам не нужны параметры, попробуйте пустой массив) для параметра.

Как ни странно, "параметры" должны быть факультативными, но по какой-то причине мой сбой не удастся, если я его оставлю.