Загрузка файла в хранилище файлов Azure с помощью node.js

Мы пытаемся создать веб-сервис для загрузки файлов в хранилище файлов Azure с помощью службы node.js.

Ниже приведен код сервера node.js.

exports.post = function(request, response){ var shareName = request.headers.sharename; var dirPath = request.headers.directorypath; var fileName = request.headers.filename; var body; var length; request.on("data", function(chunk){ body += chunk; console.log("Get data"); }); request.on("end", function(){ try{ console.log("end"); var data = body; length = data.length; console.log(body); // This giving the result as undefined console.log(length); fileService.createFileFromStream(shareName, dirPath, fileName, body, length, function(error, result, resp) { if (!error) { // file uploaded response.send(statusCodes.OK, "File Uploaded"); }else{ response.send(statusCodes.OK, "Error!"); } }); }catch (er) { response.statusCode = 400; return res.end('error: ' + er.message); } }); } 

Ниже наш клиент загружает файл.

 private static void sendPOST() throws IOException { URL obj = new URL("https://crowdtest-fileservice.azure-mobile.net/api/files_stage/"); HttpURLConnection con = (HttpURLConnection) obj.openConnection(); con.setRequestMethod("POST"); con.setRequestProperty("sharename", "newamactashare"); con.setRequestProperty("directorypath", "MaheshApp/TestLibrary/"); con.setRequestProperty("filename", "temp.txt"); Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt"); byte[] data = Files.readAllBytes(path); // For POST only - START con.setDoOutput(true); OutputStream os = con.getOutputStream(); os.write(data); os.flush(); os.close(); // For POST only - END int responseCode = con.getResponseCode(); System.out.println("POST Response Code :: " + responseCode); if (responseCode == HttpURLConnection.HTTP_OK) { // success BufferedReader in = new BufferedReader(new InputStreamReader(con.getInputStream())); String inputLine; StringBuffer response = new StringBuffer(); while ((inputLine = in.readLine()) != null) { response.append(inputLine); System.out.println(inputLine); } in.close(); // print result System.out.println(response.toString()); } else { BufferedReader br = new BufferedReader(new InputStreamReader(con.getErrorStream())); String line = ""; while ((line = br.readLine()) != null) { System.out.println(line); } System.out.println("POST request not worked"); } } 

Он показывает ошибку

Запрос «POST / api / files_stage /» истекает. Это может быть вызвано сценарием, который не отвечает на ответ, или иным образом не может вернуться из асинхронного вызова своевременно.

Обновлено:

Я также пробовал код ниже.

  var body = new Object(); body = request.body; var length = body.length; console.log(request.body); console.log(body); console.log(length); try { fileService.createFileFromStream(shareName, dirPath, fileName, body, length, function(error, result, resp) { if (!error) { // file uploaded response.send(statusCodes.OK, "File Uploaded"); }else{ response.send(statusCodes.OK, "Error!"); } }); } catch (ex) { response.send(500, { error: ex.message }); } 

Но перед лицом проблемы

{“error”: “Параметр streamа для функции createFileFromStream должен быть объектом”}

Я новичок в node.js. Пожалуйста, помогите мне исправить это.

Здесь есть несколько вопросов. Давайте перейдем через них один за другим.

1. На вашем Java-клиенте вы не можете просто сбрасывать двоичные данные в соединение мобильной связи Azure.

Причина этого в том, что мобильная служба Azure имеет два анализатора тела, которые гарантируют, что независимо от того, какой орган запроса проанализирован для вас. Итак, пока вы можете обойти парсер Express Express, указав необычный тип контента, вы все равно удалите парсер тела Azure, который испортит ваш stream данных, наивно предполагая, что это строка UTF-8.

Единственным вариантом является, таким образом, пропустить парсер Express, указав тип контента, который он не может обработать, а затем играть вместе с парсером Azure, кодируя ваши двоичные данные с помощью кодировки Base64.

Итак, в Java-клиенте замените

 Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt"); byte[] data = Files.readAllBytes(path); 

с

 con.setRequestProperty("content-type", "binary"); Path path = Paths.get("C:/Users/uma.maheshwaran/Desktop/Temp.txt"); byte[] data = Files.readAllBytes(path); data = Base64.getEncoder().encode(data); 

Если вы не используете Java 8, замените кодировщик java.util.Base64 любым другим кодировщиком Base64, к которому у вас есть доступ.

2. Функция createFileFromStream хранения createFileFromStream Azure, которую вы пытаетесь использовать, ожидает stream.

В то же время, лучшее, что вы можете получить при анализе тела запроса вручную, – это массив байтов. К сожалению, мобильные сервисы Azure используют NodeJS версии 0.8, что означает, что нет простого способа создания читаемого streamа из массива байтов, и вам придется собрать собственный stream, подходящий для Azure storage api. Некоторая клейкая лента и stream@0.0.1 должны делать только штрафы.

 var base64 = require('base64-js'), Stream = require('stream'), fileService = require('azure-storage') .createFileService('yourStorageAccount', 'yourStoragePassword'); exports.post = function (req, res) { var data = base64.toByteArray(req.body), buffer = new Buffer(data), stream = new Stream(); stream['_ended'] = false; stream['pause'] = function() { stream['_paused'] = true; }; stream['resume'] = function() { if(stream['_paused'] && !stream['_ended']) { stream.emit('data', buffer); stream['_ended'] = true; stream.emit('end'); } }; try { fileService.createFileFromStream(req.headers.sharename, req.headers.directorypath, req.headers.filename, stream, data.length, function (error, result, resp) { res.statusCode = error ? 500 : 200; res.end(); } ); } catch (e) { res.statusCode = 500; res.end(); } }; 

Это зависимости, которые вам нужны для этого образца.

 "dependencies": { "azure-storage": "^0.7.0", "base64-js": "^0.0.8", "stream": "0.0.1" } 

Если указать их в пакете package.json, вы не можете работать, вы всегда можете перейти по этой ссылке и установить их вручную через консоль.

 cd site\wwwroot npm install azure-storage npm install base64-js npm install stream@0.0.1 

3. Чтобы увеличить лимит загрузки по умолчанию в 1Mb, укажите MS_MaxRequestBodySizeKB для вашей службы.

MS_MaxRequestBodySizeKB

Имейте в виду, что, поскольку вы передаете данные в кодировке Base64, вам приходится учитывать эти накладные расходы. Таким образом, чтобы поддерживать загрузку файлов размером до 20 МБ, вы должны установить значение MS_MaxRequestBodySizeKB примерно 20 * 1024 * 4/3 = 27307.

Я считаю, что самый простой способ – использовать pkgcloud, который абстрагирует различия между облачными провайдерами, а также обеспечивает чистый интерфейс для загрузки и скачивания файлов. Он использует streamи, поэтому реализация также эффективна для памяти.

 var pkgcloud = require('pkgcloud') var fs = require('fs') var client = pkgcloud.storage.createClient({ provider: 'azure', storageAccount: 'your-storage-account', storageAccessKey: 'your-access-key' }); var readStream = fs.createReadStream('a-file.txt'); var writeStream = client.upload({ container: 'your-storage-container', remote: 'remote-file-name.txt' }); writeStream.on('error', function (err) { // handle your error case }); writeStream.on('success', function (file) { // success, file will be a File model }); readStream.pipe(writeStream); 

Мы можем использовать этот ответ streamа на SO Как отправить изображение с Android-клиента на сервер Node.js через HttpUrlConnection? , которые создают собственное промежуточное программное обеспечение для загрузки содержимого загружаемого файла в буферный массив, тогда мы можем использовать createFileFromText() для хранения файла в Azure Storage.

Вот fragment кода:

 function rawBody(req, res, next) { var chunks = []; req.on('data', function (chunk) { chunks.push(chunk); }); req.on('end', function () { var buffer = Buffer.concat(chunks); req.bodyLength = buffer.length; req.rawBody = buffer; next(); }); req.on('error', function (err) { console.log(err); res.status(500); }); } router.post('/upload', rawBody,function (req, res){ fileService.createShareIfNotExists('taskshare', function (error, result, response) { if (!error) { // if result = true, share was created. // if result = false, share already existed. fileService.createDirectoryIfNotExists('taskshare', 'taskdirectory', function (error, result, response) { if (!error) { // if result = true, share was created. // if result = false, share already existed. try { fileService.createFileFromText('taskshare', 'taskdirectory', 'test.txt', req.rawBody, function (error, result, resp) { if (!error) { // file uploaded res.send(200, "File Uploaded"); } else { res.send(200, "Error!"); } }); } catch (ex) { res.send(500, { error: ex.message }); } } }); } }); }) router.get('/getfile', function (req, res){ fileService.createReadStream('taskshare', 'taskdirectory', 'test.txt').pipe(res); }) 

Когда запрос поступает в функцию, определенную в exports.post , весь запрос уже существует, поэтому вам не нужно его буферировать. Вы можете упростить это, написав что-то в соответствии с приведенным ниже кодом.

 exports.post = function(request, response){ var shareName = request.headers.sharename; var dirPath = request.headers.directorypath; var fileName = request.headers.filename; var body = request.body; var length = body.length; console.log(length); try { fileService.createFileFromText(shareName, dirPath, fileName, body, function(error, result, resp) { if (!error) { // file uploaded response.send(statusCodes.OK, "File Uploaded"); } else { response.send(statusCodes.OK, "Error!"); } }); } catch (ex) { response.send(500, { error: ex.message }); } } 

Есть несколько вещей:

1. createFileFromText может работать с простым текстом. Но это не удастся для двоичного контента, поскольку использует кодировку UTF-8.

Вы можете захотеть обратиться к аналогичной проблеме для blob по адресу: Сохранение blob (может быть, данные!), Возвращенный вызовом AJAX для Azure Blob Storage, создает поврежденное изображение

2. API-интерфейс createFileFromStream или createWriteStreamToExistingFile \ createWriteStreamToNewFile Azure может быть createWriteStreamToNewFile .

Обратите внимание, что эти API предназначены для streamов. Вам нужно преобразовать буфер / строку в тело запроса в stream. Вы можете обратиться к разделу Как обернуть буфер как stream stream2 Readable?

Для createFileFromStream :

 fileService.createFileFromStream(req.headers.sharename, req.headers.directorypath, req.headers.filename, requestStream, data.length, function (error, result, resp) { res.statusCode = error ? 500 : 200; res.end(); } ); 

Для createWriteStreamToNewFile :

 var writeStream = fileService.createWriteStreamToNewFile(req.headers.sharename, req.headers.directorypath, req.headers.filename, data.length); requestStream.pipe(writeStream); 

3. В вашем коде есть несколько проблем.

 console.log(body); // This giving the result as undefined 

Причина в том, что вы определяете var body и оно не undefined . body += chunk кода body += chunk все равно сделает body неопределенным.

 fileService.createFileFromStream(shareName, dirPath, fileName, body, length, function(error, result, resp) { if (!error) { // file uploaded response.send(statusCodes.OK, "File Uploaded"); }else{ response.send(statusCodes.OK, "Error!"); } }); 

Когда в createFileFromStream возникает createFileFromStream , это также может быть ошибкой в ​​передаче по сети, вы также можете вернуть код ошибки вместо statusCodes.OK .