Wie schließe ich einen lesbaren Stream (vor dem Ende)?

89

Wie schließe ich einen lesbaren Stream in 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.");
   }
});

Das wäre besser als:

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

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

Dies würde den Lesevorgang jedoch nicht stoppen ...

Ionică Bizău
quelle
6
Warnung: Diese Frage bezieht sich nur auf den Kontext des fsModuls. closeexistiert nicht in Stream.Readable.
Zamnuts
2
Gute Nachrichten. Knoten Version 8 bietetstream.destroy()
joeytwiddle
Kannst du nicht anrufenreadable.push(null) && readable.destroy();
Alexander Mills

Antworten:

38

Rufen Sie auf input.close(). Es ist nicht in den Dokumenten, aber

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

macht den Job klar :) Es macht tatsächlich etwas Ähnliches wie dein isEnded.

EDIT 2015-Apr-19 Basierend auf den Kommentaren unten und zur Klarstellung und Aktualisierung:

  • Dieser Vorschlag ist ein Hack und nicht dokumentiert.
  • Zum Betrachten des Stroms lib/fs.jsfunktioniert es jedoch immer noch> 1,5 Jahre später.
  • Ich stimme dem Kommentar unten zu, destroy()dass ein Anruf vorzuziehen ist.
  • Wie unten korrekt angegeben, funktioniert dies für fs ReadStreams's, nicht für ein GenerikumReadable

Was eine generische Lösung betrifft: Es sieht nicht so aus, als ob es eine gibt, zumindest nach meinem Verständnis der Dokumentation und nach einem kurzen Blick darauf _stream_readable.js.

Mein Vorschlag wäre, Ihren lesbaren Stream in den angehaltenen Modus zu versetzen und zumindest eine weitere Verarbeitung in Ihrer Upstream-Datenquelle zu verhindern. Vergessen Sie nicht unpipe(), alle dataEreignis-Listener zu entfernen, damit sie pause()tatsächlich pausieren, wie in den Dokumenten erwähnt

Nitzan schüttelte sich
quelle
Machen Sie es länger: Fügen Sie einige Referenzen aus der Dokumentation hinzu! :-)
Ionică Bizău
1
Sehr gut! Der Quellcode scheint die beste Dokumentationsressource zu sein. ;-)
Ionică Bizău
Eigentlich würde ich lieber anrufen destroy. Zumindest heißt das so, wenn Sie autoClose auf true setzen. Wenn man sich den Quellcode (heute) ansieht, sind die Unterschiede minimal ( destroyAufrufe close), aber das könnte sich in Zukunft ändern
Marcelo Diniz
@NitzanShaked Die Datei wurde aktualisiert. Ist das der richtige Link: github.com/joyent/node/blob/… ? (Es enthält die Commit-ID, wird also in Zukunft nicht mehr geändert.)
Ionică Bizău
3
Es gibt kein close()lesbares Objekt. Gibt es eine Nie-Lösung? Mein Datenaustausch ist immer unvollständig ...
CodeManX
70

Edit: Gute Nachrichten! Ab Node.js ist 8.0.0 readable.destroyoffiziell verfügbar: https://nodejs.org/api/stream.html#stream_readable_destroy_error

ReadStream.destroy

Sie können die Funktion ReadStream.destroy jederzeit aufrufen .

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

Die öffentliche Funktion ReadStream.destroyist nicht dokumentiert (Node.js v0.12.2), aber Sie können sich den Quellcode auf GitHub ansehen ( Commit vom 5. Oktober 2012 ).

Die destroyFunktion markiert die ReadStreamInstanz intern als zerstört und ruft die closeFunktion zum Freigeben der Datei auf.

Sie können das Ereignis close abhören, um genau zu wissen, wann die Datei geschlossen wird. Das Endereignis wird erst ausgelöst, wenn die Daten vollständig verbraucht sind.


Beachten Sie, dass die destroy(und die close) Funktionen spezifisch für fs.ReadStream sind . Es gibt keinen Teil der generischen stream.readable "Schnittstelle".

Yves M.
quelle
Zumindest in der neuesten Version von Node (die anderen wurden nicht überprüft) wird der Dateideskriptor automatisch geschlossen . Trotzdem habe ich keinen gründlichen Test durchgeführt, um sicherzustellen, dass der Stream schließlich ausgelöst wird, errorwenn er nie gelesen wird. Abgesehen davon ist das einzige andere Leck, über das ich mir Sorgen machen würde, Event-Handler - auch hier bin ich mir nicht 100% sicher, aber wir könnten in Ordnung sein, wenn das Evangelium von Isaacs 2010 besagt, dass Handler es sind beschnitten, wenn Emitter gc'd sind: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil
1
Wenn die Daten zu klein sind, on('data') wird der nur einmal ausgelöst, sodass keine vorhanden .close()sind. Erinnern Sie einfach jemanden daran.
Bitfishxyz
12

Das kannst du nicht. Es gibt keine dokumentierte Möglichkeit, einen generischen lesbaren Stream ab Knoten 5.3.0 zu schließen / herunterzufahren / abzubrechen / zu zerstören . Dies ist eine Einschränkung der Node-Stream-Architektur.

Wie andere Antworten hier erklärt haben, gibt es undokumentierte Hacks für bestimmte Implementierungen von Readable, die von Node bereitgestellt werden, wie z. B. fs.ReadStream . Dies sind jedoch keine generischen Lösungen für Lesbare.

Wenn mir hier jemand das Gegenteil beweisen kann, tun Sie es bitte. Ich möchte in der Lage sein, das zu tun, was ich sage, ist unmöglich, und würde mich freuen, korrigiert zu werden.

EDIT : Hier war meine Problemumgehung: Implementieren Sie .destroy()für meine Pipeline eine komplexe Reihe von unpipe()Aufrufen. Und nach all dieser Komplexität funktioniert es nicht in allen Fällen richtig .

EDIT : Knoten V8.0.0 eine hinzugefügt destroy()api für Lesbare Ströme .

thejoshwolfe
quelle
1
Es gibt jetzt stream.pipeline, die behauptet, "Weiterleitungsfehler zu behandeln und ordnungsgemäß zu bereinigen und einen Rückruf bereitzustellen, wenn die Pipeline abgeschlossen ist". Hilft das?
Andrewdotn
11

Bei der Version wird durch 4.*.*Drücken eines Nullwerts in den Stream ein EOFSignal ausgelöst .

Aus den NodeJS-Dokumenten

Wenn ein anderer Wert als null übergeben wird, fügt die push () -Methode einen Datenblock in die Warteschlange ein, damit nachfolgende Stream-Prozessoren ihn verwenden können. Wenn null übergeben wird, signalisiert dies das Ende des Streams (EOF), wonach keine Daten mehr geschrieben werden können.

Dies funktionierte für mich, nachdem ich zahlreiche andere Optionen auf dieser Seite ausprobiert hatte.

Jacob Lowe
quelle
1
Funktioniert bei mir. Ich musste jedoch vermeiden, den Rückruf done () aufzurufen, nachdem ich null gedrückt hatte, um das erwartete Verhalten zu erhalten - nämlich, dass der gesamte Stream angehalten wird.
Rich Apodaca
5

Dieses Zerstörungsmodul soll sicherstellen, dass ein Stream zerstört wird und verschiedene APIs und Node.js-Fehler behandelt. Im Moment ist eine der besten Wahl.

NB. Ab Knoten 10 können Sie die .destroyMethode ohne weitere Abhängigkeiten verwenden.

Rocco Musolino
quelle
3

Sie können den Stream mit löschen und schließen yourstream.resume(), wodurch alles auf dem Stream abgelegt und schließlich geschlossen wird.

Aus den offiziellen Dokumenten :

readable.resume ():

Rückkehr: dies

Diese Methode bewirkt, dass der lesbare Stream die Ausgabe von 'Daten'-Ereignissen fortsetzt.

Diese Methode schaltet den Stream in den Fließmodus. Wenn Sie die Daten nicht aus einem Stream verbrauchen möchten, aber das Endereignis erreichen möchten, können Sie stream.resume () aufrufen, um den Datenfluss zu öffnen.

var readable = getReadableStreamSomehow();
readable.resume();
readable.on('end', () => {
  console.log('got to the end, but did not read anything');
});
Sayem
quelle
Dies kann als "Entleeren" des Stroms bezeichnet werden. In unserem Fall hatten wir natürlich einen 'data'Ereignis-Listener, aber wir haben dafür gesorgt, dass ein Boolescher Wert überprüft if (!ignoring) { ... }wird, damit beim Entleeren des Streams keine Daten verarbeitet werden. ignoring = true; readable.resume();
Joeytwiddle
4
Dies setzt natürlich voraus, dass der Stream 'end'irgendwann wird. Das werden nicht alle Streams! (ZB ein Stream, der das Datum jede Sekunde für immer sendet.)
joeytwiddle
3

Es ist eine alte Frage, aber auch ich habe nach der Antwort gesucht und die beste für meine Implementierung gefunden. Beide endund closeEreignisse werden ausgegeben, daher denke ich, dass dies die sauberste Lösung ist.

Dies führt den Trick in Knoten 4.4 aus. * (Stabile Version zum Zeitpunkt des Schreibens):

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

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

Eine sehr detaillierte Erklärung finden Sie unter: http://www.bennadel.com/blog/2692-you-have-to-explicitly-end-streams-after-pipes-break-in-node-js.htm

Vexter
quelle
2

Dieser Code hier macht den Trick gut:

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

writeStream.end () ist der Weg, um einen writeStream zu schließen ...

g00dnatur3
quelle
Warum erwähnen Sie, dass .end () der richtige Weg ist, aber dann verwendet Ihr Code close und destroy und nicht einmal end?
Lucas B
1
Ich schließe einen readStream im Beispiel ... einen writeStream - use.end
g00dnatur3