วิธีปิดสตรีมที่อ่านได้ (ก่อนจบ)


91

วิธีปิดสตรีมที่อ่านได้ใน 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.");
   }
});

สิ่งนี้จะดีกว่า:

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

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

แต่นี่จะไม่หยุดกระบวนการอ่าน ...


6
คำเตือน: คำถามนี้อยู่ในบริบทของfsโมดูลเท่านั้น closeไม่มีอยู่ในStream.Readable.
zamnuts

3
ข่าวดี. โหนดเวอร์ชัน 8 ให้stream.destroy()
joeytwiddle

คุณโทรหาไม่ได้readable.push(null) && readable.destroy();
Alexander Mills

คำตอบ:


40

input.close()วิงวอน ไม่ได้อยู่ในเอกสาร แต่

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

ทำงานได้อย่างชัดเจน :) มันทำสิ่งที่คล้ายกับisEndedไฟล์.

แก้ไข 2015-Apr-19ตามความคิดเห็นด้านล่างและเพื่อชี้แจงและปรับปรุง:

  • คำแนะนำนี้เป็นการแฮ็กและไม่มีการบันทึกไว้
  • แม้ว่าจะดูที่ปัจจุบันlib/fs.jsก็ยังใช้งานได้> 1.5 ปีในภายหลัง
  • ฉันเห็นด้วยกับความคิดเห็นด้านล่างเกี่ยวกับการโทรdestroy()ที่ดีกว่า
  • ตามที่ระบุไว้อย่างถูกต้องด้านล่างนี้ใช้งานได้สำหรับfs ReadStreams's ไม่ใช่ในแบบทั่วไปReadable

ในฐานะที่เป็นสำหรับการแก้ปัญหาทั่วไป: _stream_readable.jsมันไม่ได้ปรากฏเป็นถ้ามีคนอย่างน้อยจากความเข้าใจของฉันเอกสารและจากลักษณะอย่างรวดเร็ว

ข้อเสนอของฉันจะทำให้สตรีมที่อ่านได้ของคุณอยู่ในโหมดหยุดชั่วคราวอย่างน้อยที่สุดก็ป้องกันการประมวลผลเพิ่มเติมในแหล่งข้อมูลต้นน้ำของคุณ อย่าลืมunpipe()และลบตัวdataฟังเหตุการณ์ทั้งหมดเพื่อให้pause()หยุดชั่วคราวดังที่กล่าวไว้ในเอกสาร


1
อันที่จริงฉันต้องการโทรdestroyแทน อย่างน้อยนั่นคือสิ่งที่เรียกว่าหากคุณตั้งค่า autoClose เป็น true เมื่อดูที่ซอร์สโค้ด (วันนี้) ความแตกต่างมีเพียงเล็กน้อย (การdestroyโทรclose) แต่อาจเปลี่ยนแปลงได้ในอนาคต
Marcelo Diniz

ตอนนี้จำไม่ได้แล้ว แต่ดูเหมือน :)
Nitzan Shaked

4
ไม่มีclose()วัตถุที่อ่านได้ไม่มีทางแก้หรือไม่? การแลกเปลี่ยนข้อมูลของฉันไม่สมบูรณ์เสมอ ...
CodeManX

อัปเดตเพื่อชี้แจงแสดงความคิดเห็นและให้ข้อเสนอแนะ (คนยากจน) สำหรับกรณีทั่วไป แม้ว่ามันจะสมเหตุสมผลบ้างที่จะไม่บังคับให้ใช้ทั่วไปreadableในการใช้งานclose()และจัดเตรียมวิธีการเฉพาะคลาสในการดำเนินการนี้ (เช่นในกรณีนี้fsและน่าจะเป็นคลาสอื่น ๆ ที่ใช้Readable)
Nitzan Shaked

การไม่หยุดชั่วคราวจะทำให้อัพสตรีม (ผู้ส่ง) บล็อกเนื่องจากแรงดันย้อนกลับหรือทำให้บัฟเฟอร์เพิ่มขึ้นจนเกินขีด จำกัด ตามหลักการแล้วเราจะบอกผู้ส่งว่าไม่ต้องการอีกต่อไป ...
joeytwiddle

76

แก้ไข:ข่าวดี! เริ่มต้นด้วย Node.js 8.0.0 readable.destroyพร้อมใช้งานอย่างเป็นทางการ: https://nodejs.org/api/stream.html#stream_readable_destroy_error

ReadStream.destroy

คุณสามารถเรียกใช้ฟังก์ชัน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');
    });

ฟังก์ชันสาธารณะReadStream.destroyไม่ได้รับการบันทึกไว้ (Node.js v0.12.2) แต่คุณสามารถดูซอร์สโค้ดบน GitHub ได้ (คอมมิต 5 ต.ค. 2555 )

destroyฟังก์ชั่นภายในทำเครื่องหมายReadStreamเช่นเป็นทำลายและเรียกcloseฟังก์ชั่นที่จะปล่อยไฟล์

คุณสามารถฟังเหตุการณ์ปิดเพื่อทราบว่าไฟล์ถูกปิดเมื่อใด เหตุการณ์ตอนท้ายจะไม่ยิงเว้นแต่ข้อมูลที่มีการบริโภคอย่างสมบูรณ์


โปรดทราบว่าdestroy(และclose) ฟังก์ชั่นที่เฉพาะเจาะจงเพื่อfs.ReadStream ไม่มีส่วนหนึ่งของ"อินเทอร์เฟซ" stream.readableทั่วไป


อย่างน้อยในรุ่นล่าสุดของ Node (ยังไม่ได้ตรวจสอบคนอื่น ๆ ) อธิบายไฟล์จะถูกปิดโดยอัตโนมัติ ที่กล่าวว่าฉันยังไม่ได้ทำการทดสอบอย่างละเอียดเพื่อให้แน่ใจว่าสตรีมจะเริ่มทำงานในที่สุดerrorหากไม่เคยอ่าน นอกเหนือจากนั้นสิ่งเดียวที่รั่วไหลที่ฉันกังวลคือตัวจัดการเหตุการณ์ - อีกครั้งฉันไม่แน่ใจ 100% ในเรื่องนี้ แต่เราอาจจะโอเค b / c ในปี 2010 พระกิตติคุณของไอแซคกล่าวว่าผู้จัดการคือ ตัดเมื่อตัวปล่อยเป็น gc'd: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil

1
หากข้อมูลมีขนาดเล็กเกินไปon('data') จะทริกเกอร์เพียงครั้งเดียวดังนั้นจะไม่มีเลย.close()เพียงแค่เตือนคนอื่น
bitfishxyz


12

คุณทำไม่ได้ ไม่มีเอกสารวิธีปิด / ปิด / ยกเลิก / ทำลายสตรีมที่อ่านได้ทั่วไปในโหนด 5.3.0 นี่เป็นข้อ จำกัด ของสถาปัตยกรรมสตรีมโหนด

ในฐานะที่เป็นคำตอบอื่น ๆ ที่นี่ได้อธิบายมีแฮ็กที่ไม่มีเอกสารสำหรับการใช้งานที่เฉพาะเจาะจงของอ่านให้โดยโหนดเช่นfs.ReadStream นี่ไม่ใช่วิธีแก้ปัญหาทั่วไปสำหรับ Readable ใด ๆ

หากมีใครสามารถพิสูจน์ได้ว่าฉันผิดที่นี่โปรดทำ ฉันอยากจะทำในสิ่งที่ฉันบอกว่าเป็นไปไม่ได้และยินดีที่ได้รับการแก้ไข

แก้ไข : นี่คือวิธีแก้ปัญหาของฉัน: ใช้.destroy()สำหรับไปป์ไลน์ของฉันแม้ว่าจะมีการunpipe()โทรที่ซับซ้อน และหลังจากความซับซ้อนนั้นก็ไม่สามารถทำงานได้ในทุกกรณี

แก้ไข : Node v8.0.0 เพิ่มdestroy()api สำหรับสตรีมที่อ่านได้


1
ขณะนี้stream.pipelineมีซึ่งอ้างว่าจัดการ“ ข้อผิดพลาดในการส่งต่อและทำความสะอาดอย่างเหมาะสมและให้การติดต่อกลับเมื่อไปป์ไลน์เสร็จสมบูรณ์” ที่ช่วย?
andrewdotn

11

ในเวอร์ชันที่4.*.*กดค่า null เข้าไปในสตรีมจะทำให้เกิดEOFสัญญาณ

จากเอกสาร nodejs

หากส่งผ่านค่าอื่นที่ไม่ใช่ null เมธอด push () จะเพิ่มกลุ่มข้อมูลลงในคิวเพื่อให้ตัวประมวลผลสตรีมที่ตามมาใช้ หากมีการส่งค่า null จะเป็นการส่งสัญญาณการสิ้นสุดสตรีม (EOF) หลังจากนั้นจะไม่สามารถเขียนข้อมูลได้อีก

สิ่งนี้ใช้ได้ผลสำหรับฉันหลังจากลองใช้ตัวเลือกอื่น ๆ มากมายในหน้านี้


1
ใช้ได้ผลสำหรับฉัน อย่างไรก็ตามฉันจำเป็นต้องหลีกเลี่ยงการเรียกการโทรกลับ done () หลังจากกด null เพื่อให้ได้พฤติกรรมที่คาดหวังนั่นคือสตรีมทั้งหมดหยุดลง
Rich Apodaca

6

นี้ทำลายโมดูลที่มีความหมายเพื่อให้แน่ใจว่าได้รับกระแสทำลาย, การจัดการ APIs ที่แตกต่างกันและข้อบกพร่อง Node.js ตอนนี้เป็นหนึ่งในทางเลือกที่ดีที่สุด

NB. จากโหนด 10 คุณสามารถใช้.destroyวิธีนี้ได้โดยไม่ต้องพึ่งพาเพิ่มเติม


3

คุณสามารถล้างและปิดสตรีมด้วยyourstream.resume()ซึ่งจะถ่ายโอนข้อมูลทุกอย่างในสตรีมและปิดลงในที่สุด

จากเอกสารอย่างเป็นทางการ :

readable.resume ():

ผลตอบแทน: สิ่งนี้

วิธีนี้จะทำให้สตรีมที่อ่านได้กลับมาแสดงเหตุการณ์ 'ข้อมูล' อีกครั้ง

วิธีนี้จะเปลี่ยนสตรีมเป็นโหมดไหล หากคุณไม่ต้องการใช้ข้อมูลจากสตรีม แต่คุณต้องการไปที่เหตุการณ์ 'สิ้นสุด' คุณสามารถเรียกใช้ stream.resume () เพื่อเปิดการไหลของข้อมูล

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

แบบนี้เรียกว่า "ระบาย" สตรีมได้ ในกรณีของเราแน่นอนว่าเรามี'data'ผู้ฟังเหตุการณ์ แต่เราตรวจสอบบูลีนif (!ignoring) { ... }ดังนั้นมันจะไม่ประมวลผลข้อมูลเมื่อเราระบายสตรีม ignoring = true; readable.resume();
joeytwiddle

5
แน่นอนว่าสิ่งนี้ถือว่าสตรีม'end'ในบางจุด ไม่ใช่ทุกกระแสที่จะทำอย่างนั้น! (เช่นสตรีมที่ส่งวันที่ทุกวินาทีตลอดไป)
joeytwiddle

3

มันเป็นคำถามเก่า แต่ฉันก็กำลังมองหาคำตอบและพบคำถามที่ดีที่สุดสำหรับการนำไปใช้ ทั้งสองอย่างendและcloseเหตุการณ์ต่างๆถูกปล่อยออกมาดังนั้นฉันคิดว่านี่เป็นวิธีแก้ปัญหาที่สะอาดที่สุด

สิ่งนี้จะทำเคล็ดลับในโหนด 4.4 * (เวอร์ชันเสถียรในขณะที่เขียน):

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

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

สำหรับคำอธิบายโดยละเอียดโปรดดู: http://www.bennadel.com/blog/2692-you-have-to-explicitly-end-streams-after-pipes-break-in-node-js.htm


2

รหัสนี้ที่นี่จะทำเคล็ดลับได้ดี:

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

writeStream.end () เป็นวิธีไปสู่การปิด writeStream ...


1
ทำไมคุณถึงพูดถึง. end () เป็นวิธีการไป แต่โค้ดของคุณใช้ปิดและทำลายและไม่ใช้จุดสิ้นสุด
Lucas B

1
ฉันกำลังปิด readStream ในตัวอย่าง ... a writeStream - use.end
g00dnatur3
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.