Come chiudere un flusso leggibile (prima della fine)?


89

Come chiudere un flusso leggibile 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.");
   }
});

Questo sarebbe meglio di:

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

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

Ma questo non fermerebbe il processo di lettura ...


6
Attenzione: questa domanda è solo nel contesto del fsmodulo. closenon esiste in Stream.Readable.
Zamnuts

3
Buone notizie. La versione 8 del nodo forniscestream.destroy()
joeytwiddle

non puoi chiamarereadable.push(null) && readable.destroy();
Alexander Mills

Risposte:


39

Invoca input.close(). Non è nei documenti, ma

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

chiaramente fa il lavoro :) In realtà fa qualcosa di simile al tuo isEnded.

EDIT 2015-Apr-19 Sulla base dei commenti di seguito e per chiarire e aggiornare:

  • Questo suggerimento è un hack e non è documentato.
  • Anche se per guardare la corrente lib/fs.jsfunziona ancora> 1,5 anni dopo.
  • Sono d'accordo con il commento sotto sulla chiamata destroy()come preferibile.
  • Come correttamente affermato di seguito, questo funziona per fs ReadStreams's, non su un genericoReadable

Per quanto riguarda una soluzione generica: non sembra che ce ne sia una, almeno dalla mia comprensione della documentazione e da una rapida occhiata _stream_readable.js.

La mia proposta sarebbe quella di mettere il tuo flusso leggibile in modalità in pausa , impedendo almeno un'ulteriore elaborazione nella tua origine dati a monte. Non dimenticare di unpipe()rimuovere tutti i datalistener di eventi in modo che si pause()interrompa effettivamente, come indicato nella documentazione


Allungalo: aggiungi alcuni riferimenti dalla documentazione! :-)
Ionică Bizău

2
Molto bene! Il codice sorgente sembra essere la migliore risorsa di documentazione. ;-)
Ionică Bizău

In realtà preferirei chiamare destroyinvece. Almeno questo è ciò che viene chiamato se imposti autoClose su true. Guardando il codice sorgente (oggi) le differenze sono minime ( destroychiamate close) ma potrebbero cambiare in futuro
Marcelo Diniz

@NitzanShaked Il file è stato aggiornato. È questo il collegamento corretto: github.com/joyent/node/blob/… ? (contiene l'id del commit, quindi non verrà modificato in futuro)
Ionică Bizău,

4
Non c'è un close()oggetto Leggibile, c'è una soluzione mai? Il mio scambio di dati è sempre incompleto ...
CodeManX

71

Modifica: buone notizie! A partire da Node.js 8.0.0 readable.destroyè ufficialmente disponibile: https://nodejs.org/api/stream.html#stream_readable_destroy_error

ReadStream.destroy

Puoi chiamare la funzione ReadStream.destroy in qualsiasi momento.

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 funzione pubblica ReadStream.destroynon è documentata (Node.js v0.12.2) ma puoi dare un'occhiata al codice sorgente su GitHub ( commit del 5 ottobre 2012 ).

La destroyfunzione contrassegna internamente l' ReadStreamistanza come distrutta e chiama la closefunzione per rilasciare il file.

Puoi ascoltare l' evento di chiusura per sapere esattamente quando il file viene chiuso. L' evento finale non si attiverà a meno che i dati non siano completamente consumati.


Notare che le destroy(e le close) funzioni sono specifiche di fs.ReadStream . Non ci sono parti della generica "interfaccia" stream.readable .


Almeno nell'ultima versione di Node (non ho controllato gli altri), il descrittore di file viene chiuso automaticamente . Detto questo, non ho eseguito alcun tipo di test approfondito per assicurarmi che il flusso alla fine si attivi errorse non viene mai letto. A parte questo, l'unica altra fuga di notizie di cui mi preoccuperei sono i gestori di eventi - ancora una volta, non ne sono sicuro al 100%, ma potremmo essere ok b / c il 2010 il gospel di Isaacs dice che i gestori sono potatura quando gli emettitori sono sottoposti a gc: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil

1
Se i dati sono troppo piccoli, on('data') si attiverà solo una volta, quindi non ce ne saranno .close(), basta ricordarlo a qualcun altro.
bitfishxyz


12

Non puoi. Non esiste un modo documentato per chiudere / arrestare / interrompere / distruggere un Readable generico flusso partire dal nodo 5.3.0. Questa è una limitazione dell'architettura del flusso di nodi.

Come altre risposte qui hanno spiegato, ci sono hack non documentati per implementazioni specifiche di Readable fornite da Node, come fs.ReadStream . Queste non sono soluzioni generiche per alcun Readable però.

Se qualcuno può dimostrare che ho torto qui, per favore fallo. Vorrei poter fare quello che dico è impossibile e sarei felice di essere corretto.

EDIT : Ecco la mia soluzione alternativa: implementare .destroy()per la mia pipeline anche se una complessa serie di unpipe()chiamate. E dopo tutta quella complessità, non funziona correttamente in tutti i casi .

EDIT : il nodo v8.0.0 ha aggiunto un'API destroy()per flussi leggibili .


1
C'è ora stream.pipeline, che afferma di gestire "errori di inoltro e ripulire correttamente e fornire una richiamata quando la pipeline è completa". Questo aiuta?
andrewdotn

11

Alla versione, 4.*.*spingere un valore nullo nel flusso attiverà un EOFsegnale.

Dai documenti di nodejs

Se viene passato un valore diverso da null, il metodo push () aggiunge un blocco di dati nella coda affinché i successivi stream processor li consumino. Se viene passato null, segnala la fine del flusso (EOF), dopodiché non è più possibile scrivere dati.

Questo ha funzionato per me dopo aver provato numerose altre opzioni in questa pagina.


1
Per me va bene. Tuttavia, avevo bisogno di evitare di chiamare il callback done () dopo aver premuto null per ottenere il comportamento previsto, ovvero che l'intero flusso si interrompe.
Rich Apodaca

6

Questo distrugge modulo di scopo di garantire che un flusso venga distrutto, gestendo diverse API e bug di Node.js. In questo momento è una delle scelte migliori.

NB. Dal nodo 10 è possibile utilizzare il .destroymetodo senza ulteriori dipendenze.


3

Puoi cancellare e chiudere lo stream con yourstream.resume() , che scaricherà tutto sullo stream e alla fine lo chiuderà.

Dai documenti ufficiali :

readable.resume ():

Ritorno: questo

Questo metodo farà sì che il flusso leggibile riprenda a emettere eventi "dati".

Questo metodo commuterà il flusso in modalità flusso. Se non vuoi consumare i dati da un flusso, ma vuoi arrivare al suo evento "finale", puoi chiamare stream.resume () per aprire il flusso di dati.

var readable = getReadableStreamSomehow();
readable.resume();
readable.on('end', () => {
  console.log('got to the end, but did not read anything');
});

Questo può essere chiamato "drenaggio" del flusso. Nel nostro caso, ovviamente avevamo un 'data'listener di eventi, ma l'abbiamo fatto controllare un booleano in if (!ignoring) { ... }modo che non elaborerà i dati quando stiamo prosciugando il flusso. ignoring = true; readable.resume();
joeytwiddle,

5
Ovviamente questo presuppone che lo stream prima o poi lo farà 'end'. Non tutti i flussi lo faranno! (Ad esempio, un flusso che invia la data ogni secondo, per sempre.)
joeytwiddle

3

È una vecchia domanda ma anch'io stavo cercando la risposta e ho trovato la migliore per la mia implementazione. Entrambi endeclose gli eventi vengono emessi in modo che questa sia la soluzione più pulita.

Questo farà il trucco nel nodo 4.4. * (Versione stabile al momento della scrittura):

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

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

Per una spiegazione molto dettagliata vedere: http://www.bennadel.com/blog/2692-you-have-to-explicitly-end-streams-after-pipes-break-in-node-js.htm


2

Questo codice qui farà bene il trucco:

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

writeStream.end () è il modo migliore per chiudere un writeStream ...


Perché dici che .end () è il modo migliore, ma poi il tuo codice usa close e destroy e non usa nemmeno end?
Lucas B

1
sto chiudendo un readStream nell'esempio ... un writeStream - usa.end
g00dnatur3
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.