Recherche…


Paramètres

Paramètre Définition
Flux lisible type de flux où les données peuvent être lues
Courant inscriptible type de flux où les données peuvent être écrites
Flux duplex type de flux à la fois lisible et inscriptible
Transformer le flux type de flux duplex pouvant transformer les données en cours de lecture puis d'écriture

Lire des données depuis TextFile avec des flux

Les E / S dans le nœud sont asynchrones. Ainsi, interagir avec le disque et le réseau implique de transmettre des rappels aux fonctions. Vous pourriez être tenté d'écrire du code qui sert un fichier à partir du disque comme ceci:

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    fs.readFile(__dirname + '/data.txt', function (err, data) {
        res.end(data);
    });
});
server.listen(8000);

Ce code fonctionne mais il est encombrant et stocke en mémoire tout le fichier data.txt pour chaque requête avant de renvoyer le résultat aux clients. Si le fichier data.txt est très volumineux, votre programme pourrait commencer à consommer beaucoup de mémoire car il est utilisé simultanément par de nombreux utilisateurs, en particulier pour les utilisateurs dont les connexions sont lentes.

L'expérience utilisateur est également médiocre car les utilisateurs devront attendre que tout le fichier soit mis en mémoire tampon sur votre serveur avant de pouvoir recevoir du contenu.

Heureusement, les deux arguments (req, res) sont des flux, ce qui signifie que nous pouvons écrire cela beaucoup mieux en utilisant fs.createReadStream () au lieu de fs.readFile ():

var http = require('http');
var fs = require('fs');

var server = http.createServer(function (req, res) {
    var stream = fs.createReadStream(__dirname + '/data.txt');
    stream.pipe(res);
});
server.listen(8000);

Ici, .pipe () prend en charge l'écoute des événements 'data' et 'end' du fichier fs.createReadStream (). Ce code n'est pas seulement plus propre, mais maintenant, le fichier data.txt sera écrit sur les clients, un morceau à la fois, dès qu'ils sont reçus du disque.

Cours d'eau

Les flux lisibles peuvent être "canalisés" ou connectés à des flux inscriptibles. Cela rend le flux de données du flux source vers le flux de destination sans trop d'effort.

var fs = require('fs')

var readable = fs.createReadStream('file1.txt')
var writable = fs.createWriteStream('file2.txt')

readable.pipe(writable) // returns writable

Lorsque des flux inscriptibles sont également des flux lisibles, c'est-à-dire lorsqu'ils sont des flux duplex , vous pouvez continuer à les transmettre à d'autres flux inscriptibles.

var zlib = require('zlib')

fs.createReadStream('style.css')
  .pipe(zlib.createGzip()) // The returned object, zlib.Gzip, is a duplex stream.
  .pipe(fs.createWriteStream('style.css.gz')

Les flux lisibles peuvent également être acheminés vers plusieurs flux.

var readable = fs.createReadStream('source.css')
readable.pipe(zlib.createGzip()).pipe(fs.createWriteStream('output.css.gz'))
readable.pipe(fs.createWriteStream('output.css')

Notez que vous devez accéder aux flux de sortie de manière synchrone (en même temps) avant tout flux de données. Sinon, des données incomplètes pourraient être transmises.

Notez également que les objets de flux peuvent émettre error événements d' error ; Assurez-vous de gérer ces événements de manière responsable sur chaque flux, selon vos besoins:

var readable = fs.createReadStream('file3.txt')
var writable = fs.createWriteStream('file4.txt')
readable.pipe(writable)
readable.on('error', console.error)
writable.on('error', console.error)

Création de votre propre flux lisible / inscriptible

Nous verrons les objets de flux renvoyés par des modules tels que fs, etc., mais si nous voulons créer notre propre objet pouvant être diffusé.

Pour créer un objet Stream, nous devons utiliser le module de flux fourni par NodeJs

    var fs = require("fs");
    var stream = require("stream").Writable;
    
    /* 
     *  Implementing the write function in writable stream class.
     *  This is the function which will be used when other stream is piped into this 
     *  writable stream.
     */
    stream.prototype._write = function(chunk, data){
        console.log(data);
    }
    
    var customStream = new stream();
    
    fs.createReadStream("am1.js").pipe(customStream);

Cela nous donnera notre propre flux accessible en écriture. nous pouvons implémenter n'importe quoi dans la fonction _write . La méthode ci-dessus fonctionne dans la version NodeJs 4.xx mais dans NodeJs 6.x ES6 introduit des classes, donc la syntaxe a changé. Voici le code pour la version 6.x de NodeJs

    const Writable = require('stream').Writable;
    
    class MyWritable extends Writable {
      constructor(options) {
        super(options);
      }
    
      _write(chunk, encoding, callback) {
        console.log(chunk);
      }
    }

Pourquoi Streams?

Examinons les deux exemples suivants pour lire le contenu d'un fichier:

Le premier, qui utilise une méthode asynchrone pour lire un fichier et fournit une fonction de rappel qui est appelée une fois que le fichier est entièrement lu dans la mémoire:

fs.readFile(`${__dirname}/utils.js`, (err, data) => {
  if (err) {
    handleError(err);
  } else {
    console.log(data.toString());
  }
})

Et le second, qui utilise des streams pour lire le contenu du fichier, pièce par pièce:

var fileStream = fs.createReadStream(`${__dirname}/file`);
var fileContent = '';
fileStream.on('data', data => {
  fileContent += data.toString();
})

fileStream.on('end', () => {
  console.log(fileContent);
})

fileStream.on('error', err => {
  handleError(err)
})

Il convient de mentionner que les deux exemples font exactement la même chose . Quelle est la différence alors?

  • Le premier est plus court et semble plus élégant
  • La seconde vous permet de faire un traitement sur le fichier en cours de lecture (!)

Lorsque les fichiers que vous traitez sont petits, il n'y a pas d'effet réel lors de l'utilisation de streams , mais que se passe-t-il lorsque le fichier est volumineux? (si grand qu'il faut 10 secondes pour le lire en mémoire)

Sans streams vous attendez, ne faites absolument rien (à moins que votre processus ne fasse autre chose), jusqu'à ce que les 10 secondes passent et que le fichier soit entièrement lu , et alors seulement vous pouvez commencer à traiter le fichier.

Avec les streams , vous obtenez le contenu du fichier pièce par pièce, exactement quand ils sont disponibles - et cela vous permet de traiter le fichier pendant sa lecture.


L'exemple ci-dessus n'illustre pas la manière dont les streams peuvent être utilisés pour des tâches qui ne peuvent pas être effectuées lors du rappel, alors examinons un autre exemple:

Je voudrais télécharger un fichier gzip , le décompresser et enregistrer son contenu sur le disque. Étant donné l' url du fichier, c'est ce qui doit être fait:

  • Télécharger le fichier
  • Décompressez le fichier
  • Enregistrez-le sur le disque

Voici un [petit fichier] [1], stocké dans mon stockage S3 . Le code suivant effectue les opérations ci-dessus dans le mode de rappel.

var startTime = Date.now()
s3.getObject({Bucket: 'some-bucket', Key: 'tweets.gz'}, (err, data) => {
  // here, the whole file was downloaded

  zlib.gunzip(data.Body, (err, data) => {
    // here, the whole file was unzipped

    fs.writeFile(`${__dirname}/tweets.json`, data, err => {
      if (err) console.error(err)

      // here, the whole file was written to disk
      var endTime = Date.now()
      console.log(`${endTime - startTime} milliseconds`) // 1339 milliseconds
    })
  })
})

// 1339 milliseconds

Voici à quoi cela ressemble en utilisant des streams :

s3.getObject({Bucket: 'some-bucket', Key: 'tweets.gz'}).createReadStream()
  .pipe(zlib.createGunzip())
  .pipe(fs.createWriteStream(`${__dirname}/tweets.json`));

// 1204 milliseconds

Oui, ce n'est pas plus rapide lorsque vous traitez de petits fichiers - le fichier testé pèse 80KB . Tester cela sur un fichier plus gros, de 71MB gzippé ( 71MB Mo 382MB ), montre que la version des streams est beaucoup plus rapide

  • Il a fallu 20925 millisecondes pour télécharger 71MB , décompressez-le, puis écrivez 382MB sur le disque - en utilisant le mode de rappel .
  • En comparaison, il a fallu 13434 millisecondes pour faire la même chose en utilisant la version de streams (35% plus rapide, pour un fichier pas si gros).


Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow