Подтвердить что ты не робот

Загрузка файла в хранилище файлов 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/" истекает. Это должно быть вызванный script, который не может записать ответ, иначе не может вернуться из асинхронного вызова своевременно.

Обновлено:

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

  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. Пожалуйста, помогите мне исправить это.

4b9b3361

Ответ 1

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

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, что означает, что нет простого способа создания читаемого потока из массива байтов, и вам придется собрать собственный поток, подходящий для Azure storage api. Некоторая клейкая лента и [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. Чтобы увеличить лимит загрузки по умолчанию в 1Mb, укажите MS_MaxRequestBodySizeKB для вашей службы.

MS_MaxRequestBodySizeKB

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

Ответ 2

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

Я считаю, что самый простой способ - использовать 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);

Ответ 4

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

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

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);
})

Ответ 5

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

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

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

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

Обратите внимание, что эти API предназначены для потоков. Вам нужно преобразовать буфер/строку в тело запроса в поток. Вы можете ссылаться на Как обернуть буфер как поток Read2 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 undefined.

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.