Jak zamknąć czytelny strumień (przed końcem)?

90

Jak zamknąć czytelny strumień w 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.");
   }
});

To byłoby lepsze niż:

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

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

Ale to nie zatrzymałoby procesu czytania ...

Ionică Bizău
źródło
6
Ostrzeżenie: to pytanie jest tylko w kontekście fsmodułu. closenie istnieje w Stream.Readable.
zamnuts
3
Dobre wieści. Węzeł w wersji 8 zapewniastream.destroy()
joeytwiddle
nie możesz zadzwonićreadable.push(null) && readable.destroy();
Alexander Mills

Odpowiedzi:

39

Wywołaj input.close(). Nie ma tego w dokumentach, ale

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

najwyraźniej spełnia swoje zadanie :) W rzeczywistości robi coś podobnego do twojego isEnded.

EDYCJA 2015-kwiecień-19 Na podstawie poniższych komentarzy oraz wyjaśnienia i aktualizacji:

  • Ta sugestia to hack i nie została udokumentowana.
  • Chociaż patrząc na prąd lib/fs.js, nadal działa> 1,5 roku później.
  • Zgadzam się z poniższym komentarzem na temat destroy()preferowanego dzwonienia .
  • Jak poprawnie napisano poniżej, działa to dla fs ReadStreams's, a nie dla generycznychReadable

Jeśli chodzi o ogólne rozwiązanie: nie wydaje się, żeby takie istniało, przynajmniej z mojego zrozumienia dokumentacji i szybkiego spojrzenia _stream_readable.js.

Moja propozycja polegałaby na przestawieniu czytelnego strumienia w tryb wstrzymania , co najmniej zapobiegając dalszemu przetwarzaniu w źródle danych. Nie zapomnij unpipe()i usuń wszystkie datadetektory zdarzeń, aby pause()faktycznie zostały wstrzymane, jak wspomniano w dokumentacji

Nitzan Shaked
źródło
Wydłuż to: dodaj kilka odniesień z dokumentacji! :-)
Ionică Bizău
2
Bardzo dobrze! Kod źródłowy wydaje się być najlepszym źródłem dokumentacji. ;-)
Ionică Bizău
Właściwie wolałbym destroyzamiast tego zadzwonić . Przynajmniej tak się nazywa, jeśli ustawisz autoClose na true. Patrząc na kod źródłowy (dzisiaj) różnice są minimalne ( destroywywołania close), ale to może się zmienić w przyszłości
Marcelo Diniz
@NitzanShaked Plik został zaktualizowany. Czy to jest właściwy link: github.com/joyent/node/blob/… ? (zawiera identyfikator zmiany, więc nie zostanie zmieniony w przyszłości)
Ionică Bizău
4
Nie ma close()na obiekt czytelny, czy nigdy nie ma rozwiązania? Moja wymiana danych jest zawsze niepełna ...
CodeManX
74

Edycja: Dobra wiadomość! Począwszy od Node.js 8.0.0 readable.destroyjest oficjalnie dostępny: https://nodejs.org/api/stream.html#stream_readable_destroy_error

ReadStream.destroy

W dowolnym momencie możesz wywołać funkcję ReadStream.destroy .

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

Funkcja publiczna ReadStream.destroynie jest udokumentowana (Node.js v0.12.2), ale możesz rzucić okiem na kod źródłowy na GitHub ( zatwierdzenie 5 października 2012 ).

destroyFunkcja wewnętrznie zaznaczyć ReadStreaminstancję jako zniszczone i wywołuje closefunkcję, aby zwolnić plik.

Możesz słuchać zdarzenia close, aby dokładnie wiedzieć, kiedy plik zostanie zamknięty. Wydarzenie koniec nie zadziała, chyba że dane są całkowicie zużyte.


Zauważ, że funkcje destroy(i close) są specyficzne dla fs.ReadStream . Nie ma części ogólnego "interfejsu" stream.readable .

Yves M.
źródło
Przynajmniej w najnowszej wersji Node (nie sprawdzałem innych) deskryptor pliku jest automatycznie zamykany . To powiedziawszy, nie przeprowadziłem żadnego dokładnego testu, aby upewnić się, że strumień w końcu errorzostanie uruchomiony, jeśli nigdy nie zostanie odczytany. Poza tym jedynym innym przeciekiem, o który się martwię, są programy obsługi zdarzeń - po raz kolejny nie jestem tego w 100% pewien, ale możemy być w porządku przed 2010 rokiem, według ewangelii Izaaka przycinane, gdy emitery są gc'd: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil
1
Jeśli dane są zbyt małe, on('data') wyzwolą się tylko raz, więc nie będzie żadnych .close(), po prostu przypomnij komuś innemu.
bitfishxyz
12

Nie możesz. Nie ma udokumentowanego sposobu na zamknięcie / zamknięcie / przerwanie / zniszczenie ogólnego strumienia do odczytu od Węzła 5.3.0. Jest to ograniczenie architektury strumienia Node.

Jak wyjaśniły inne odpowiedzi tutaj, istnieją nieudokumentowane hacki dla określonych implementacji Readable dostarczanych przez Node, takich jak fs.ReadStream . Nie są to jednak ogólne rozwiązania dla żadnego Readable.

Jeśli ktoś może mi tutaj udowodnić, że się mylę, zrób to. Chciałbym móc robić to, co mówię, jest niemożliwe i byłbym zachwycony, gdybym został skorygowany.

EDYCJA : Oto moje obejście: zaimplementuj .destroy()dla mojego potoku przez złożoną serię unpipe()wywołań. Po całej tej złożoności nie we wszystkich przypadkach działa poprawnie .

EDYCJA : Węzeł v8.0.0 dodał destroy()interfejs API dla strumieni czytelnych .

thejoshwolfe
źródło
1
Jest teraz stream.pipeline, który twierdzi, że radzi sobie z „błędami przekazywania i prawidłowym czyszczeniem oraz zapewnia wywołanie zwrotne po zakończeniu potoku”. To pomaga?
andrewdotn
11

W wersji 4.*.*wstawienie wartości null do strumienia wyzwoli EOFsygnał.

Z dokumentacji nodejs

Jeśli przekazana zostanie wartość inna niż null, metoda push () dodaje porcję danych do kolejki w celu wykorzystania przez kolejne procesory strumieniowe. Jeśli zostanie przekazana wartość null, sygnalizuje koniec strumienia (EOF), po którym nie można zapisać więcej danych.

To zadziałało dla mnie po wypróbowaniu wielu innych opcji na tej stronie.

Jacob Lowe
źródło
1
Pracuje dla mnie. Jednak musiałem uniknąć wywołania wywołania zwrotnego done () po wypchnięciu wartości null, aby uzyskać oczekiwane zachowanie - a mianowicie zatrzymanie całego strumienia.
Rich Apodaca
6

Ten moduł niszczenia ma na celu zapewnienie zniszczenia strumienia, obsługując różne interfejsy API i błędy Node.js. W tej chwili jest to jeden z najlepszych wyborów.

NB. W węźle 10 możesz używać tej .destroymetody bez dalszych zależności.

Rocco Musolino
źródło
3

Możesz wyczyścić i zamknąć strumień yourstream.resume(), co spowoduje zrzucenie wszystkiego w strumieniu i ostatecznie zamknięcie go.

Z oficjalnych dokumentów :

readable.resume ():

Powrót: to

Ta metoda spowoduje, że czytelny strumień wznowi emitowanie zdarzeń „danych”.

Ta metoda przełączy strumień w tryb płynny. Jeśli nie chcesz zużywać danych ze strumienia, ale chcesz dostać się do jego zdarzenia „końca”, możesz wywołać funkcję stream.resume (), aby otworzyć przepływ danych.

var readable = getReadableStreamSomehow();
readable.resume();
readable.on('end', () => {
  console.log('got to the end, but did not read anything');
});
Sayem
źródło
Można to nazwać „osuszaniem” strumienia. W naszym przypadku oczywiście mieliśmy 'data'detektor zdarzeń, ale sprawiliśmy, że sprawdzał wartość logiczną, if (!ignoring) { ... }aby nie przetwarzał danych podczas opróżniania strumienia. ignoring = true; readable.resume();
joeytwiddle
5
Oczywiście zakłada to, że strumień będzie 'end'w pewnym momencie. Nie wszystkie strumienie to zrobią! (Np. Strumień, który wysyła datę co sekundę, na zawsze.)
joeytwiddle
3

To stare pytanie, ale ja również szukałem odpowiedzi i znalazłem najlepsze dla mojej realizacji. Emitowane są zarówno zdarzenia, jak endi closezdarzenia, więc myślę, że jest to najczystsze rozwiązanie.

To załatwi sprawę w węźle 4.4. * (Stabilna wersja w momencie pisania):

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

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

Bardzo szczegółowe wyjaśnienie można znaleźć pod adresem : http://www.bennadel.com/blog/2692-you-have-to-explicitly-end-streams-after-pipes-break-in-node-js.htm

Vexter
źródło
2

Ten kod tutaj załatwi sprawę:

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

writeStream.end () to najlepszy sposób na zamknięcie metody writeStream ...

g00dnatur3
źródło
1
Dlaczego wspominasz, że .end () jest rozwiązaniem, które należy wykonać, ale w takim razie w Twoim kodzie używa się close i zniszcz, a nawet nie używa końca?
Lucas B
1
zamykam readStream w przykładzie ... writeStream - użyj.end
g00dnatur3