Загрузите файл через веб-сервис и отправьте его в хранилище BLOB-объектов Azure через Node/Express

Мне нужно извлечь файлы из веб-службы поставщика и поместить их в уникальный контейнер BLOB-объектов, чтобы у пользователей было уникальное "рабочее пространство". По сути, я получал файлы от поставщика, и пользователи могли редактировать их через файлы в своем собственном контейнере больших двоичных объектов, чтобы они не пересекали рабочие файлы друг друга. У меня работают уникальные контейнеры BLOB-объектов, но мне нужно "скачать"/ получить файлы из API моего поставщика и поместить их в контейнер BLOB-объектов. Я могу успешно получить файлы, которые будут отдельными вызовами для получения PDF, текстовых файлов и изображений... но если я пытаюсь загрузить их в хранилище BLOB-объектов Azure, я получаю следующую ошибку в Node.js:

TypeError: Невозможно прочитать свойство 'length' со значением NULL

Я думаю, что мне нужно кодировать файлы как base64 на стороне клиента, чтобы правильно получить длину, и я видел несколько примеров использования Canvas с toDataURL, но я не уверен, является ли это лучшим методом для загрузки и передачи непосредственно в Хранилище BLOB-объектов Azure, тем более что у меня есть такие файлы, как PDF (не уверен, что PDF-файлы могут быть закодированы в формате base64).

Вот мой контроллер AngularJS, который вызывает службу (обратите внимание, что фактическая конечная точка может меняться в зависимости от того, какие файлы они вызывают, поэтому я использую GET файлов на стороне клиента для управления переменными, которые пользователь может вводить в форме):

$scope.getFiles = function () {

$.ajax({
url: 'http://vendorwebservice.net/ws/file1',
type: "GET",
success: function (result) {
console.log(result);
var filename = 'Texture_0.png';

$http.post('/postFile', { filename: filename, file: result }).success(function (data) {
console.log(data);
}, function (err) {
console.log(err);
});

alert("Files Retrieved!");
},
error: function (error) {
console.log("Failed to download image!");
}
})
}

Вот мой бэкэнд / узел / экспресс-код:

app.post('/postFile', function (req, res, next) {
    var filename = req.body.filename;
    var file = req.body.file;
    var base64Data;
    fileBuffer = decodeBase64Image(file);
    blobSvc.createBlockBlobFromText('blob5', filename, fileBuffer.data, { 'contentType': fileBuffer.type }, function (error, result, response) {
        if (!error) {
            console.log("Uploaded" + result);
        }
        else {
            console.log(error);
        }
    });
})

// Decode file for upload
function decodeBase64Image(dataString) {
    var matches = dataString.match(/^data:([A-Za-z-+\/]+);base64,(.+)$/),
        response = {};

    if (matches.length !== 3) {
        return new Error('Invalid input string');
    }

    response.type = matches[1];
    response.data = new Buffer(matches[2], 'base64');

    return response;
}

Обновление 1: По предложению Гэри я попробовал следующее, но немного испортил код, так как в API моего поставщика нет файловых URI, а есть конечные точки, которые создают файл на GET (иначе, я теряюсь, как передать конечную точку в сравнении с примером Гэри, что имеет смысл). Например, моя конечная точка поставщика " http://vendorapi.net/ws/texture_0" возвращает файл с именем "Texture_0.png".

Угловой код переднего конца:

 $scope.getFromVendor = function () {
            var filename = 'Texture_0.png';//jpg,txt...
            $http.post('/uploadapifiles', { filename: filename, url: 'http://vendorapi.net/ws/texture_0' }).success(function (data) {
                console.log(data);
            }, function (err) {
                console.log(err);
            });
        }

Обработка загрузки на стороне сервера (я считаю, что это самая запутанная:

app.get(http://vendorapi.net/ws/texture_0', function (req, res, next) {
    res.download('http://vendorapi.net/ws/texture_0' + req.params.filename);
})

Обработка загрузки на стороне сервера:

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

    var filename = req.body.filename;
    var r = request(req.body.url).pipe(fs.createWriteStream('http://vendorapi.net/ws/texture_0' + filename))
    r.on('close', function () {
        blobsrv.createBlockBlobFromLocalFile('blob5', filename, 'http://vendorapi.net/ws/texture_0' + filename, function (error, result, response) {
            if (!error) {
                console.log("Uploaded" + result);
            }
            else {
                console.log(error);
            }
        });
    })
});

1 ответ

Решение

По своей первоначальной идее вы сначала получаете данные содержимого файла на стороне клиента, а затем публикуете их на веб-сервере Express.

Если файл, который вы получаете, имеет большой размер, это замедлит работу вашего сайта из-за того, что данные файла будут переданы дважды по HTTP, и могут возникнуть другие проблемы.

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

Поэтому я попробовал другую идею в качестве обходного пути.

Я просто публикую API-интерфейс для получения определенного файла на сервер, перетащил файл как файл в каталог сервера и загрузил файл в хранилище на стороне сервера. Вот мой фрагмент кода:

Угловая передняя часть:

$scope.upload =function(){
    var filename = (new Date()).getTime()+'.pdf';//jpg,txt...
    $http.post('http://localhost:1337/uploadfile', { filename: filename, url: 'http://localhost:1337/output/123.pdf'}).success(function (data) {
        console.log(data);
    },function(err){
        console.log(err);
    });
  }

Бэк-энд:

Я подозреваю, что API, который вы получите в форме файла, будет таким же.

router.get('/output/:filename', function (req, res, next) {
    res.download('upload/'+req.params.filename);
})

Обработчик пост-запроса использует пакетный запрос, и нет необходимости выяснять тип файла или тип кодировки, createBlockBlobFromLocalFile загрузит файл в указанное вами место в хранилище BLOB-объектов, ссылка на API:

router.post('/uploadfile', function (req, res, next) {
    var request = require('request');
    var filename = req.body.filename;
    var tmpFolder = 'upload/', //this folder is to save files download from vendor URL, and should be created in the root directory previously. 
        tmpFileSavedLocation = tmpFolder + filename; //this is the location of download files, e.g. 'upload/Texture_0.png'
    var r = request(req.body.url).pipe(fs.createWriteStream(tmpFileSavedLocation))//this syntax will download file from the URL and save in the location asyns
   r.on('close', function (){
        blobsrv.createBlockBlobFromLocalFile('vsdeploy', filename, tmpFileSavedLocation, function (error, result, response) {
            if (!error) {
                console.log("Uploaded" + result);
           }
            else {
                console.log(error);
            }
        });
    })

})
Другие вопросы по тегам