Chargement d'un fichier dans le stockage de fichiers Azure à l'aide de node.js

nous essayons de créer un service web pour télécharger des fichiers vers le stockage de fichiers Azure à l'aide de node.js service.

ci-Dessous est le nœud.js code serveur.

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

});

}

ci-Dessous notre client pour télécharger un fichier.

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

C'est montrer l'erreur

la requête 'POST /api/files_stage/' est chronométrée. Cela pourrait être causée par un script qui ne parvient pas à écrire à la réponse, ou autrement ne revient pas d'un appel asynchrone dans un délai manière.

mise à Jour:

j'ai aussi essayé le code ci-dessous.

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

mais faire face au problème

{"error":"le Paramètre de flux pour la fonction createFileFromStream devrait être un objet"}

je suis nouveau à node.js. Merci de m'aider à résoudre ce problème.

17
demandé sur James Z 2015-12-17 13:12:16

5 réponses

Il y a plusieurs question ici. Passons-les en revue un par un.

1. Dans votre client Java, vous ne pouvez pas simplement transférer les données binaires dans une connexion de service mobile Azure.

la raison en est qu'un service mobile Azure a deux analyseurs de corps qui s'assurent que peu importe quoi, le corps de la requête est analysé pour vous. Ainsi, alors que vous pouvez vous promener dans L'analyseur de corps Express en spécifiant un type de contenu peu commun, vous continuerez à frapper le corps D'Azur analyseur qui va gâcher votre flux de données en supposant naïvement qu'il s'agit d'une chaîne UTF-8.

la seule option est donc de sauter L'analyseur Express en spécifiant un type de contenu qu'il ne peut pas gérer, puis de jouer avec L'analyseur Azure en encodant vos données binaires avec l'encodage Base64.

Donc, dans le client Java remplacer

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

si vous n'êtes pas sur Java 8, remplacez le java.util.Encodeur Base64 avec une autre Base64 encodeur vous avez accès.

2. createFileFromStream fonction api de stockage Azure vous essayez d'utiliser expects a stream.

en même temps, le meilleur que vous pouvez obtenir en analysant un corps de requête manuellement est un tableau d'octets. Malheureusement, les services mobiles Azure utilisent la version 0.8 de NodeJS, ce qui signifie qu'il n'y a pas de moyen facile de construire un flux lisible à partir d'un tableau d'octets, et vous devrez assembler votre propre flux adapté à L'api de stockage Azure. Certains de la bande de conduit et stream@0.0.1 doit faire bien.

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

ce sont les dépendances vous avez besoin pour cet échantillon.

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

Si précisant dans votre service.json ne fonctionne pas, vous pouvez toujours revenir à cette lien et les installer manuellement via la console.

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

3. Pour augmenter la limite de téléchargement par défaut de 1Mb, spécifiez MS_MaxRequestBodySizeKB pour votre service.

MS_MaxRequestBodySizeKB

n'oubliez pas cependant que puisque vous transférez vos données en tant que Base64-encoded, vous devez tenir compte de cette charge. Ainsi, pour soutenir le téléchargement de fichiers jusqu'à 20Mb dans la taille, vous devez définir MS_MaxRequestBodySizeKB à peu près 20 * 1024 * 4 / 3 = 27307.

7
répondu Roman Pletnev 2016-01-07 09:32:12

je trouve le plus simple est d'utiliser pkgcloud qui simplifie les différences entre les fournisseurs de cloud et fournit également une interface propre pour le téléchargement et le téléchargement de fichiers. Il utilise des flux de sorte que la mise en œuvre est efficace de mémoire aussi bien.

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
répondu Pier-Luc Gendreau 2015-12-17 21:53:52

nous pouvons tirer profit de cette réponse du thread sur SO comment envoyer une image D'un client Android à un noeud.js server via HttpUrlConnection? créer un middleware pour obtenir le téléchargement de fichiers contenu dans un tableau tampon, alors nous pouvons utiliser createFileFromText() pour stocker le fichier en Stockage Azure.

Voici l'extrait de code:

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
répondu Gary Liu - MSFT 2017-05-23 12:24:19

Lorsque la demande arrive à la fonction définie dans exports.post, l'ensemble de la demande est déjà là, donc vous n'avez pas besoin de tampon. Vous pouvez le simplifier en écrivant quelque chose dans le sens du code ci-dessous.

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
répondu carlosfigueira 2015-12-22 15:33:09

Il y a plusieurs choses:

1. createFileFromText peut fonctionner avec du texte simple. Mais il échouera pour ces contenus binaires, car il utilise l'encodage UTF-8.

Vous pourriez vouloir vous référer à la même question pour les blob: sauver blob (pourrait être data!) retourné par L'appel AJAX à Azure Blob Storage crée une image corrompue

2. createFileFromStream ou createWriteStreamToExistingFile\createWriteStreamToNewFile Azure storage API peut être la fonction peut aider.

veuillez noter que ces IPA sont ciblés par les flux. Vous devez convertir votre tampon / chaîne de caractères dans le corps de la requête en un flux. Vous pouvez vous référer à comment envelopper un tampon comme un flux lisible par 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. Il y a plusieurs problèmes dans votre code

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

la raison est que vous définissez var body et c'est undefined. Le code body += chunk toujours 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!");
  }
});

Quand l'erreur se produit dans createFileFromStream, cela pourrait aussi être une erreur dans le transfert réseau, vous pourriez aussi vouloir retourner le code d'erreur au lieu de statusCodes.OK.

0
répondu Yang Xia - Microsoft 2017-05-23 11:53:13