Comment fermer un flux lisible (avant la fin)?

89

Comment fermer un flux lisible dans Node.js?

var input = fs.createReadStream('lines.txt');

input.on('data', function(data) {
   // after closing the stream, this will not
   // be called again

   if (gotFirstLine) {
      // close this stream and continue the
      // instructions from this if
      console.log("Closed.");
   }
});

Ce serait mieux que:

input.on('data', function(data) {
   if (isEnded) { return; }

   if (gotFirstLine) {
      isEnded = true;
      console.log("Closed.");
   }
});

Mais cela n'empêcherait pas le processus de lecture ...

Ionică Bizău
la source
6
Attention: cette question est uniquement dans le cadre du fsmodule. closen'existe pas en Stream.Readable.
zamnuts
3
Bonnes nouvelles. Node version 8 fournitstream.destroy()
joeytwiddle
ne peux-tu pas appelerreadable.push(null) && readable.destroy();
Alexander Mills

Réponses:

39

Invoquez input.close(). Ce n'est pas dans la documentation, mais

https://github.com/joyent/node/blob/cfcb1de130867197cbc9c6012b7e84e08e53d032/lib/fs.js#L1597-L1620

fait clairement le travail :) Il fait quelque chose de similaire au vôtre isEnded.

EDIT 2015-Apr-19 Basé sur les commentaires ci-dessous, et pour clarifier et mettre à jour:

  • Cette suggestion est un hack et n'est pas documentée.
  • Bien que pour regarder le courant, lib/fs.jscela fonctionne toujours> 1,5 ans plus tard.
  • Je suis d'accord avec le commentaire ci-dessous sur le fait que l'appel destroy()est préférable.
  • Comme indiqué correctement ci-dessous, cela fonctionne pour fs ReadStreamsles, pas sur un génériqueReadable

Quant à une solution générique: il ne semble pas y en avoir une, du moins d'après ma compréhension de la documentation et d'un rapide coup d'œil _stream_readable.js.

Ma proposition serait de mettre votre flux lisible en mode suspendu , empêchant au moins un traitement ultérieur dans votre source de données en amont. N'oubliez pas de unpipe()supprimer tous les dataécouteurs d'événements pour qu'ils pause()se mettent en pause, comme indiqué dans la documentation

Nitzan secoué
la source
Rallongez-le: ajoutez quelques références de la documentation! :-)
Ionică Bizău
2
Très bien! Le code source semble être la meilleure ressource de documentation. ;-)
Ionică Bizău
En fait, je préférerais appeler à la destroyplace. Du moins, c'est ce qu'on appelle si vous définissez autoClose sur true. En regardant le code source (aujourd'hui) les différences sont minimes ( destroyappels close) mais cela pourrait changer à l'avenir
Marcelo Diniz
@NitzanShaked Le fichier a été mis à jour. Est-ce le bon lien: github.com/joyent/node/blob/… ? (il contient l'identifiant de validation, il ne sera donc pas modifié à l'avenir)
Ionică Bizău
4
Il n'y a pas close()d'objet lisible, n'y a-t-il jamais de solution? Mon échange de données est toujours incomplet ...
CodeManX
71

Edit: Bonne nouvelle! À partir de Node.js 8.0.0 readable.destroyest officiellement disponible: https://nodejs.org/api/stream.html#stream_readable_destroy_error

ReadStream.destroy

Vous pouvez appeler la fonction ReadStream.destroy à tout moment.

var fs = require('fs');

var readStream = fs.createReadStream('lines.txt');
readStream
    .on('data', function (chunk) {
        console.log(chunk);
        readStream.destroy();
    })
    .on('end', function () {
        // This may not been called since we are destroying the stream
        // the first time 'data' event is received
        console.log('All the data in the file has been read');
    })
    .on('close', function (err) {
        console.log('Stream has been destroyed and file has been closed');
    });

La fonction publique ReadStream.destroyn'est pas documentée (Node.js v0.12.2) mais vous pouvez consulter le code source sur GitHub ( validation du 5 octobre 2012 ).

La destroyfonction marque en interne l' ReadStreaminstance comme détruite et appelle la closefonction pour libérer le fichier.

Vous pouvez écouter l' événement de fermeture pour savoir exactement quand le fichier est fermé. L' événement de fin ne se déclenchera que si les données sont complètement consommées.


Notez que les destroy(et les close) fonctions sont spécifiques à fs.ReadStream . Il n'y a pas de partie de l ' "interface" stream.readable générique .

Yves M.
la source
Au moins dans la dernière version de Node (je n'ai pas vérifié les autres), le descripteur de fichier se ferme automatiquement . Cela dit, je n'ai effectué aucun test approfondi pour m'assurer que le flux se déclenche éventuellement errors'il n'est jamais lu. En dehors de cela, la seule autre fuite dont je m'inquiéterais concerne les gestionnaires d'événements - encore une fois, je ne suis pas sûr à 100% à ce sujet, mais nous pourrions être d'accord car l'Évangile d'Isaacs de 2010 dit que les gestionnaires sont élagué lorsque les émetteurs sont gc'd: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil
1
Si les données sont trop petites, le on('data') ne se déclenchera qu'une seule fois, donc il n'y en aura pas .close(), rappelez-le simplement à quelqu'un d'autre.
bitfishxyz
12

Vous ne pouvez pas. Il n'existe aucun moyen documenté de fermer / arrêter / abandonner / détruire un flux lisible générique à partir de Node 5.3.0. Il s'agit d'une limitation de l'architecture de flux Node.

Comme d'autres réponses l'ont expliqué ici, il existe des hacks non documentés pour des implémentations spécifiques de Readable fournies par Node, telles que fs.ReadStream . Ce ne sont pas des solutions génériques pour tout Readable.

Si quelqu'un peut me prouver le contraire, faites-le. J'aimerais pouvoir faire ce que je dis est impossible, et serais ravi d'être corrigé.

EDIT : Voici ma solution de contournement: implémenter .destroy()pour mon pipeline via une série complexe d' unpipe()appels. Et après toute cette complexité, cela ne fonctionne pas correctement dans tous les cas .

EDIT : Node v8.0.0 a ajouté une destroy()API pour les flux lisibles .

thejoshwolfe
la source
1
Il y a maintenant stream.pipeline, qui prétend gérer "les erreurs de transfert et nettoyer correctement et fournir un rappel lorsque le pipeline est terminé." Est ce que ça aide?
andrewdotn
11

À la version, 4.*.*pousser une valeur nulle dans le flux déclenchera un EOFsignal.

À partir de la documentation nodejs

Si une valeur autre que null est transmise, la méthode push () ajoute un morceau de données dans la file d'attente pour les processeurs de flux suivants à consommer. Si nul est passé, il signale la fin du flux (EOF), après quoi aucune donnée ne peut plus être écrite.

Cela a fonctionné pour moi après avoir essayé de nombreuses autres options sur cette page.

Jacob Lowe
la source
1
Travaille pour moi. Cependant, je devais éviter d'appeler le rappel done () après avoir poussé null pour obtenir le comportement attendu, à savoir que tout le flux s'arrête.
Rich Apodaca le
6

Ce module de destruction est destiné à garantir qu'un flux est détruit, en traitant différentes API et bogues Node.js. En ce moment, c'est l'un des meilleurs choix.

NB. Depuis Node 10, vous pouvez utiliser la .destroyméthode sans autres dépendances.

Rocco Musolino
la source
3

Vous pouvez effacer et fermer le flux avec yourstream.resume() , qui videra tout sur le flux et le fermera éventuellement.

À partir des documents officiels :

readable.resume ():

Retour: ce

Cette méthode provoquera la reprise par le flux lisible des événements «data».

Cette méthode fera passer le flux en mode fluide. Si vous ne souhaitez pas consommer les données d'un flux, mais que vous souhaitez accéder à son événement «end», vous pouvez appeler stream.resume () pour ouvrir le flux de données.

var readable = getReadableStreamSomehow();
readable.resume();
readable.on('end', () => {
  console.log('got to the end, but did not read anything');
});
Sayem
la source
Cela peut être appelé "drainer" le flux. Dans notre cas, bien sûr, nous avions un 'data'écouteur d'événements, mais nous l'avons fait vérifier un booléen if (!ignoring) { ... }afin qu'il ne traite pas les données lorsque nous vidons le flux. ignoring = true; readable.resume();
joeytwiddle
5
Bien sûr, cela suppose que le flux le fera 'end'à un moment donné. Tous les streams ne le feront pas! (Par exemple, un flux qui envoie la date toutes les secondes, pour toujours.)
joeytwiddle
3

C'est une vieille question mais moi aussi je cherchais la réponse et j'ai trouvé la meilleure pour ma mise en œuvre. Les deux endetclose événements sont émis, donc je pense que c'est la solution la plus propre.

Cela fera l'affaire dans le nœud 4.4. * (Version stable au moment de l'écriture):

var input = fs.createReadStream('lines.txt');

input.on('data', function(data) {
   if (gotFirstLine) {
      this.end(); // Simple isn't it?
      console.log("Closed.");
   }
});

Pour une explication très détaillée, voir: http://www.bennadel.com/blog/2692-you-have-to-explicitly-end-streams-after-pipes-break-in-node-js.htm

Vexter
la source
2

Ce code ici fera très bien l'affaire:

function closeReadStream(stream) {
    if (!stream) return;
    if (stream.close) stream.close();
    else if (stream.destroy) stream.destroy();
}

writeStream.end () est le moyen idéal pour fermer un writeStream ...

g00dnatur3
la source
Pourquoi dites-vous que .end () est le moyen de prédilection, mais alors votre code utilise close and destroy et n'utilise même pas end?
Lucas B
1
Je ferme un readStream dans l'exemple ... un writeStream - use.end
g00dnatur3