web-dev-qa-db-fra.com

Envoi de données d'image volumineuses via HTTP dans Node.js

Dans mon environnement de développement, j'ai deux serveurs. L'un envoie une image à l'autre via une requête http POST http.

Le serveur client fait ceci:

    fs.readFile(rawFile.path,'binary',function (err, file){
        restler.post("http://0.0.0.0:5000",{
            data: file,
            headers:{
                "Content-Type": rawFile.type,
            }
        }).on('complete',function(data,response){                               
            console.log(data);
            res.send("file went through")
        })

Le serveur qui reçoit la demande fait ceci:

    server.post('/',function(req,res,next){
        fs.writeFileSync("test.png",req.body,"binary",function(err){
            if(err) throw err;
            res.send("OK")
        })
    })

Si j'envoie une petite image, cela fonctionne bien. Cependant, si j’envoie une grande image bien que le fichier soit enregistré correctement, seule la première partie supérieure de l’image est affichée. Le reste est noir. La taille de l'image est correcte.

Je suppose que c'est juste le premier morceau de l'image qui est écrit sur le fichier . J'ai essayé de créer un readStream et un writeStream mais cela ne semble pas fonctionner:

req.body.pipe(fs.createWriteStream('test.png'))

Puis-je diffuser directement à partir des données binaires et pipe dans le fichier? Pour ce que j'ai vu, readStream est souvent utilisé pour diffuser à partir de fichiers et non de données binaires brutes.

Je lis quelques post s mais cela ne semble pas fonctionner pour moi.

J'utilise le module restler dans le serveur client et restify dans l'autre.

Merci!

28
Maroshii

Désolé d'être franc, mais il y a beaucoup de choses qui ne vont pas ici.

readFile lit le tout le contenu d'un fichier en mémoire avant d'appeler le rappel, moment où vous commencez à télécharger le fichier.

C'est mauvais, surtout lorsqu'il s'agit de gros fichiers comme des images, car il n'y a vraiment aucune raison de lire le fichier en mémoire. C'est du gaspillage; et sous charge, vous constaterez que votre serveur manquera de mémoire et plantera.

Au lieu de cela, vous souhaitez obtenir un flux, qui émet des fragments de données au fur et à mesure qu'ils sont lus à partir du disque. Tout ce que vous avez à faire est de transférer ces fragments dans votre flux de téléchargement (pipe), puis de supprimer les données de la mémoire. De cette manière, vous n'utilisez jamais plus qu'une petite quantité de mémoire tampon.

(Le comportement par défaut d'un flux lisible est de traiter des données binaires brutes; c'est uniquement si vous transmettez un encoding qu'il traite le texte.)

Le module request rend cela particulièrement facile:

fs.createReadStream('test.png').pipe(request.post('http://0.0.0.0:5000/'));

Sur le serveur, vous avez un problème plus important. N'utilisez jamais les méthodes * Sync. Cela empêche votre serveur d'effectuer n'importe quoi (comme pour répondre à d'autres requêtes) jusqu'à ce que le fichier entier soit vidé sur le disque, ce qui peut prendre quelques secondes.

Au lieu de cela, nous voulons prendre le flux de données entrant et le diriger vers un flux de système de fichiers. Vous étiez sur la bonne voie à l’origine; La raison pour laquelle req.body.pipe(fs.createWriteStream('test.png')) n'a pas fonctionné est parce que body n'est pas un flux.

body est généré par le middleware bodyParser. Dans restify, ce middleware agit comme readFile en ce sens qu'il met en mémoire tampon l'entité de requête entrante entière. Dans ce cas, nous ne voulons pas cela. Désactivez le middleware d'analyseur de corps.

Alors, où est le flux de données entrant? C'est l'objet req lui-même. Request de restify hérite du http.IncomingMessage du noeud, qui est un flux lisible. Alors:

fs.createWriteStream('test.png').pipe(req);

Je devrais également mentionner que tout cela fonctionne tellement simplement parce qu'il n'y a aucune surcharge d'analyse de formulaire impliquée. request envoie simplement le fichier sans multipart/form-data wrappers:

POST / HTTP/1.1
Host: localhost:5000
content-type: application/octet-stream
Connection: keep-alive
Transfer-Encoding: chunked

<image data>...

Cela signifie qu'un navigateur ne peut pas publier de fichier sur cette URL. Si cela est nécessaire, recherchez formidable , qui analyse en continu les entités de requête.

63
josh3736

Je ne sais pas grand chose sur Restler. Mais poster une image est une requête en plusieurs parties.

restler.post("http://0.0.0.0:5000",{
    data: restler.file(path, filename, fileSize, encoding, contentType),
    multipart: true
})
0
Jean-Philippe Leclerc