it-swarm.com.ru

Загрузка файла в хранилище файлов 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": "Поток параметров для функции createFileFromStream должен быть объектом"}

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

18
Sniper

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

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

Причина этого заключается в том, что в мобильной службе Azure есть два синтаксических анализатора тела, которые гарантируют, что независимо от того, что, тело запроса будет проанализировано для вас . Таким образом, вы можете обойти анализатор тела Express, указав необычный тип контента, вы все равно попадете в анализатор тела Azure, который испортит ваш поток данных, наивно полагая, что это строка 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. Функция API хранилища createFileFromStream Azure, которую вы пытаетесь использовать, ожидает поток.

В то же время лучшее, что вы можете получить при синтаксическом анализе тела запроса, - это байтовый массив. К сожалению, мобильные сервисы Azure используют NodeJS версии 0.8, что означает, что нет простого способа создать читаемый поток из байтового массива, и вам придется собирать собственный поток, подходящий для API хранилища Azure. Некоторая клейкая лента и [email protected] должны подойти.

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 [email protected]

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

 MS_MaxRequestBodySizeKB

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

8
Roman Pletnev

Когда запрос поступает к функции, определенной в 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 });
    }
}
3
carlosfigueira

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

Вот фрагмент кода:

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);
})
3
Gary Liu - MSFT

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

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);
3
Pier-Luc Gendreau

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

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

Возможно, вы захотите сослаться на похожую проблему для BLOB-объекта по адресу: Сохранение BLOB-объекта (возможно, данных!), Возвращаемого вызовом AJAX в хранилище BLOB-объектов Azure, создает поврежденное изображение

2. Может помочь функция createFileFromStream или createWriteStreamToExistingFile\createWriteStreamToNewFile API хранилища Azure.

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

Для 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 неопределенным.

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, это также может быть ошибка при передаче по сети, вы также можете вернуть код ошибки вместо statusCodes.OK.

0
Yang Xia - Microsoft