Bagaimana cara menutup aliran yang dapat dibaca (sebelum akhir)?

90

Bagaimana cara menutup aliran yang dapat dibaca di 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.");
   }
});

Ini akan lebih baik daripada:

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

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

Tetapi ini tidak akan menghentikan proses membaca ...

Ionică Bizău
sumber
6
Peringatan: Pertanyaan ini hanya dalam konteks fsmodul. closetidak ada di Stream.Readable.
zamnuts
3
Kabar baik. Node versi 8 menyediakanstream.destroy()
joeytwiddle
tidak bisakah Anda meneleponreadable.push(null) && readable.destroy();
Alexander Mills

Jawaban:

39

Panggil input.close(). Ini tidak ada di dokumen, tapi

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

jelas melakukan pekerjaan itu :) Ini sebenarnya melakukan sesuatu yang mirip dengan Anda isEnded.

EDIT 2015-Apr-19 Berdasarkan komentar di bawah, dan untuk memperjelas dan memperbarui:

  • Saran ini adalah retasan, dan tidak didokumentasikan.
  • Meskipun untuk melihat saat lib/fs.jsini masih berfungsi> 1,5 tahun kemudian.
  • Saya setuju dengan komentar di bawah tentang panggilan destroy()yang lebih disukai.
  • Seperti yang dinyatakan dengan benar di bawah ini, ini berfungsi untuk fs ReadStreams's, bukan pada generikReadable

Adapun solusi umum: tampaknya tidak ada, setidaknya dari pemahaman saya tentang dokumentasi dan dari pandangan sekilas _stream_readable.js.

Proposal saya akan membuat streaming Anda yang dapat dibaca dalam mode dijeda , setidaknya mencegah pemrosesan lebih lanjut di sumber data upstream Anda. Jangan lupa untuk unpipe()menghapus semua dataevent listener sehingga pause()benar - benar berhenti, seperti yang disebutkan dalam dokumen

Nitzan Terguncang
sumber
Buat lebih panjang: tambahkan beberapa referensi dari dokumentasi! :-)
Ionică Bizău
2
Baik sekali! Kode sumber tampaknya menjadi sumber dokumentasi terbaik. ;-)
Ionică Bizău
Sebenarnya saya lebih suka menelepon destroy. Setidaknya itulah yang disebut jika Anda menyetel autoClose ke true. Dengan melihat kode sumber (hari ini) perbedaannya minimal ( destroypanggilan close) tetapi itu bisa berubah di masa depan
Marcelo Diniz
@NitzanShaked File telah diupdate. Apakah ini tautan yang benar: github.com/joyent/node/blob/… ? (ini berisi id komit, jadi tidak akan diubah di masa depan)
Ionică Bizău
4
Tidak ada close()on object Readable, apakah tidak pernah ada solusi? Pertukaran data saya selalu tidak lengkap ...
CodeManX
74

Edit: Kabar baik! Dimulai dengan Node.js 8.0.0 readable.destroysecara resmi tersedia: https://nodejs.org/api/stream.html#stream_readable_destroy_error

ReadStream.destroy

Anda dapat memanggil fungsi ReadStream.destroy kapan saja.

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

Fungsi publik ReadStream.destroytidak didokumentasikan (Node.js v0.12.2) tetapi Anda dapat melihat kode sumber di GitHub ( komit 5 Oktober 2012 ).

The destroyfungsi internal menandai ReadStreammisalnya sebagai hancur dan memanggil closefungsi untuk melepaskan file.

Anda dapat mendengarkan event close untuk mengetahui dengan tepat kapan file tersebut ditutup. Peristiwa akhir tidak akan diaktifkan kecuali data dikonsumsi sepenuhnya.


Perhatikan bahwa fungsi destroy(dan close) khusus untuk fs.ReadStream . Tidak ada bagian dari "antarmuka" stream.readable generik .

Yves M.
sumber
Setidaknya di Node versi terbaru (belum memeriksa yang lain), deskriptor file ditutup secara otomatis . Meskipun demikian, saya belum melakukan pengujian menyeluruh apa pun untuk memastikan bahwa aliran pada akhirnya akan aktif errorjika tidak pernah dibaca. Selain itu, satu-satunya kebocoran lain yang saya khawatirkan adalah penangan acara-- sekali lagi, saya tidak 100% yakin akan hal ini, tapi kita mungkin baik-baik saja karena Injil Isaacs tahun 2010 mengatakan bahwa penangannya adalah dipangkas ketika emitter di-gc'd: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil
1
Jika data terlalu kecil, on('data') hanya akan terpicu sekali, jadi tidak akan ada .close(), ingatkan saja orang lain.
bitfishxyz
12

Tidak boleh. Tidak ada cara yang terdokumentasi untuk menutup / mematikan / membatalkan / menghancurkan aliran Readable generik pada Node 5.3.0. Ini adalah batasan dari arsitektur aliran Node.

Seperti jawaban lain yang dijelaskan di sini, ada peretasan tidak berdokumen untuk implementasi spesifik dari Readable yang disediakan oleh Node, seperti fs.ReadStream . Ini bukan solusi umum untuk semua Readable.

Jika seseorang bisa membuktikan saya salah di sini, tolong lakukan. Saya ingin dapat melakukan apa yang saya katakan tidak mungkin, dan dengan senang hati akan dikoreksi.

EDIT : Inilah solusi saya: terapkan .destroy()untuk pipeline saya melalui serangkaian unpipe()panggilan yang rumit . Dan setelah semua kerumitan itu, itu tidak berfungsi dengan baik di semua kasus .

EDIT : Node v8.0.0 menambahkan destroy()api untuk aliran yang Dapat Dibaca .

thejoshwolfe
sumber
1
Sekarang ada stream.pipeline, yang mengklaim menangani "kesalahan penerusan dan pembersihan yang benar serta memberikan callback saat pipeline selesai". Apakah itu membantu?
andrewdotn
11

Pada versi yang 4.*.*mendorong nilai null ke dalam aliran akan memicu EOFsinyal.

Dari dokumen nodejs

Jika nilai selain null diteruskan, metode push () menambahkan potongan data ke dalam antrean untuk digunakan pemroses streaming berikutnya. Jika null dilewatkan, itu menandakan akhir aliran (EOF), setelah itu tidak ada lagi data yang dapat ditulis.

Ini berhasil untuk saya setelah mencoba berbagai opsi lain di halaman ini.

Jacob Lowe
sumber
1
Bekerja untuk saya. Namun, saya perlu menghindari pemanggilan callback done () setelah mendorong null untuk mendapatkan perilaku yang diharapkan - yaitu seluruh streaming berhenti.
Kaya Apodaca
6

Ini menghancurkan modul dimaksudkan untuk memastikan aliran akan hancur, penanganan API yang berbeda dan bug Node.js. Saat ini adalah salah satu pilihan terbaik.

NB. Dari Node 10 Anda dapat menggunakan .destroymetode ini tanpa ketergantungan lebih lanjut.

Rocco Musolino
sumber
3

Anda dapat menghapus dan menutup aliran dengan yourstream.resume(), yang akan membuang semua yang ada di aliran dan akhirnya menutupnya.

Dari dokumen resmi :

readable.resume ():

Kembali: ini

Metode ini akan menyebabkan aliran yang dapat dibaca melanjutkan acara 'data' yang dipancarkan.

Metode ini akan mengalihkan aliran ke mode mengalir. Jika Anda tidak ingin menggunakan data dari aliran, tetapi Anda ingin mendapatkan acara 'akhir', Anda dapat memanggil stream.resume () untuk membuka aliran data.

var readable = getReadableStreamSomehow();
readable.resume();
readable.on('end', () => {
  console.log('got to the end, but did not read anything');
});
Sayem
sumber
Ini bisa disebut "menguras" arus. Dalam kasus kami, tentu saja kami memiliki 'data'pendengar acara, tetapi kami membuatnya memeriksa boolean if (!ignoring) { ... }sehingga tidak akan memproses data saat kami menguras aliran. ignoring = true; readable.resume();
joeytwiddle
5
Tentu saja ini mengasumsikan aliran akan 'end'di beberapa titik. Tidak semua aliran bisa melakukannya! (Misalnya aliran yang mengirimkan tanggal setiap detik, selamanya.)
joeytwiddle
3

Ini pertanyaan lama tapi saya juga sedang mencari jawabannya dan menemukan yang terbaik untuk implementasi saya. Keduanya enddan closeacara dipancarkan jadi saya pikir ini adalah solusi terbersih.

Ini akan melakukan trik di node 4.4. * (Versi stabil pada saat penulisan):

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

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

Untuk penjelasan yang sangat rinci lihat: http://www.bennadel.com/blog/2692-you-have-to-explicitly-end-streams-after-pipes-break-in-node-js.htm

Vexter
sumber
2

Kode ini di sini akan melakukan trik dengan baik:

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

writeStream.end () adalah cara masuk untuk menutup writeStream ...

g00dnatur3
sumber
1
Mengapa Anda menyebutkan .end () adalah cara masuk, tetapi kemudian kode Anda menggunakan tutup dan hancurkan dan bahkan tidak menggunakan akhir?
Lucas B
1
saya menutup readStream dalam contoh ... a writeStream - gunakan.end
g00dnatur3