Làm cách nào để đóng một luồng có thể đọc được (trước khi kết thúc)?


89

Làm cách nào để đóng một luồng có thể đọc được trong 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.");
   }
});

Điều này sẽ tốt hơn:

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

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

Nhưng điều này sẽ không dừng quá trình đọc ...


6
Cảnh báo: Câu hỏi này chỉ trong ngữ cảnh của fsmô-đun. closekhông tồn tại trong Stream.Readable.
zamnuts

2
Tin tốt. Node phiên bản 8 cung cấpstream.destroy()
joeytwiddle

bạn không thể gọireadable.push(null) && readable.destroy();
Alexander Mills

Câu trả lời:


39

Kêu gọi input.close(). Nó không có trong tài liệu, nhưng

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

rõ ràng thực hiện công việc :) Nó thực sự làm một cái gì đó tương tự như của bạn isEnded.

CHỈNH SỬA 2015-04-19 Dựa trên nhận xét bên dưới, và để làm rõ và cập nhật:

  • Đề xuất này là một cuộc tấn công, và không được ghi lại.
  • Mặc dù nhìn vào hiện tại, lib/fs.jsnó vẫn hoạt động> 1,5 năm sau đó.
  • Tôi đồng ý với nhận xét dưới đây về việc gọi destroy()là thích hợp hơn.
  • Như đã nêu một cách chính xác bên dưới, điều này hoạt động cho fs ReadStreams's, không phải cho mộtReadable

Đối với một giải pháp chung chung: nó không xuất hiện như thể có một, ít nhất là từ sự hiểu biết của tôi về tài liệu và từ một cái nhìn nhanh _stream_readable.js.

Đề xuất của tôi sẽ đặt luồng có thể đọc của bạn ở chế độ bị tạm dừng , ít nhất là ngăn việc xử lý thêm trong nguồn dữ liệu ngược dòng của bạn. Đừng quên unpipe()và xóa tất cả trình datanghe sự kiện để pause()thực sự tạm dừng, như đã đề cập trong tài liệu


Làm cho nó dài hơn: thêm một số tham khảo từ tài liệu! :-)
Ionică Bizău

2
Rất tốt! Mã nguồn dường như là nguồn tài liệu tốt nhất. ;-)
Ionică Bizău

Thực ra tôi thích gọi điện hơn destroy. Ít nhất đó là những gì được gọi nếu bạn đặt Tự động đóng thành true. Bằng cách nhìn vào mã nguồn (hôm nay) sự khác biệt là rất nhỏ ( destroycuộc gọi close) nhưng điều đó có thể thay đổi trong tương lai
Marcelo Diniz

@NitzanShaked Tệp đã được cập nhật. Đây có phải là liên kết chính xác không: github.com/joyent/node/blob/… ? (nó chứa id cam kết, vì vậy nó sẽ không bị thay đổi trong tương lai)
Ionică Bizău

4
Không có close()trên đối tượng Readable, có một giải pháp không bao giờ? Trao đổi dữ liệu của tôi luôn luôn là không đầy đủ ...
CodeManX

71

Chỉnh sửa: Tin tốt! Bắt đầu với Node.js 8.0.0 readable.destroychính thức có sẵn: https://nodejs.org/api/stream.html#stream_readable_destroy_error

ReadStream.destroy

Bạn có thể gọi hàm ReadStream.destroy bất kỳ lúc nào.

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

Chức năng công khai ReadStream.destroykhông được ghi lại (Node.js v0.12.2) nhưng bạn có thể xem mã nguồn trên GitHub ( cam kết ngày 5 tháng 10 năm 2012 ).

Các destroychức năng trong nội bộ đánh dấu ReadStreamdụ như bị phá hủy và gọi closechức năng để giải phóng các tập tin.

Bạn có thể nghe sự kiện đóng để biết chính xác thời điểm đóng tệp. Sự kiện kết thúc sẽ không kích hoạt trừ khi dữ liệu được sử dụng hoàn toàn.


Lưu ý rằng destroy(và các close) hàm dành riêng cho fs.ReadStream . Không có một phần nào của "giao diện" stream . có thể đọc chung.


Ít nhất trong phiên bản Node mới nhất (chưa kiểm tra các phiên bản khác), trình mô tả tệp sẽ tự động đóng . Điều đó nói rằng, tôi chưa thực hiện bất kỳ loại kiểm tra kỹ lưỡng nào để đảm bảo rằng luồng cuối cùng sẽ kích hoạt errornếu nó không bao giờ được đọc. Bên cạnh đó, rò rỉ duy nhất khác mà tôi lo lắng là trình xử lý sự kiện - một lần nữa, tôi không chắc chắn 100% về điều này, nhưng chúng ta có thể ổn. B / c năm 2010 phúc âm của Isaacs nói rằng trình xử lý là bị cắt khi bộ phát được gc'd: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil

1
Nếu dữ liệu quá nhỏ, on('data') sẽ chỉ kích hoạt một lần, vì vậy sẽ không có bất kỳ .close(), chỉ cần nhắc nhở người khác.
bitfishxyz


12

Bạn không thể. Không có tài liệu nào về cách đóng / tắt máy / hủy bỏ / hủy một luồng có thể đọc chung kể từ Nút 5.3.0. Đây là một hạn chế của kiến ​​trúc luồng Node.

Như các câu trả lời khác ở đây đã giải thích, có các bản hack không có giấy tờ cho các triển khai cụ thể của Readable do Node cung cấp, chẳng hạn như fs.ReadStream . Tuy nhiên, đây không phải là các giải pháp chung cho bất kỳ Readable nào.

Nếu ai đó có thể chứng minh tôi sai ở đây, xin vui lòng làm. Tôi muốn có thể làm những gì tôi nói là không thể và rất vui khi được sửa chữa.

CHỈNH SỬA : Đây là cách giải quyết của tôi: triển khai .destroy()cho đường ống của tôi thông qua một loạt unpipe()lệnh gọi phức tạp . Và sau tất cả sự phức tạp đó, nó không hoạt động đúng trong mọi trường hợp .

CHỈNH SỬA : Node v8.0.0 đã thêm một destroy()api cho các luồng có thể đọc được .


1
Hiện có stream.pipeline, công cụ này tuyên bố sẽ xử lý "lỗi chuyển tiếp và dọn dẹp đúng cách và cung cấp cuộc gọi lại khi đường dẫn hoàn tất." cái đó có giúp ích không?
andrewdotn

11

Tại phiên bản, 4.*.*việc đẩy giá trị null vào luồng sẽ kích hoạt một EOFtín hiệu.

Từ tài liệu nodejs

Nếu một giá trị khác null được truyền, thì phương thức push () sẽ thêm một đoạn dữ liệu vào hàng đợi để các bộ xử lý luồng tiếp theo sử dụng. Nếu null được chuyển qua, nó báo hiệu sự kết thúc của luồng (EOF), sau đó không thể ghi thêm dữ liệu nào nữa.

Điều này đã làm việc cho tôi sau khi thử nhiều tùy chọn khác trên trang này.


1
Làm việc cho tôi. Tuy nhiên, tôi cần tránh gọi lại lệnh gọi done () sau khi đẩy null để có được hành vi mong đợi - cụ thể là toàn bộ luồng tạm dừng.
Rich Apodaca

6

Đây phá hủy mô-đun có nghĩa là để đảm bảo một dòng suối bị phá hủy, xử lý các API khác nhau và lỗi Node.js. Ngay bây giờ là một trong những sự lựa chọn tốt nhất.

NB. Từ Node 10, bạn có thể sử dụng .destroyphương thức này mà không cần phụ thuộc thêm.


3

Bạn có thể xóa và đóng luồng bằng yourstream.resume(), thao tác này sẽ hủy mọi thứ trên luồng và cuối cùng đóng nó.

Từ các tài liệu chính thức :

readable.resume ():

Trở lại: cái này

Phương pháp này sẽ làm cho luồng có thể đọc tiếp tục phát ra các sự kiện 'dữ liệu'.

Phương pháp này sẽ chuyển dòng sang chế độ chảy. Nếu bạn không muốn sử dụng dữ liệu từ một luồng, nhưng bạn muốn đến sự kiện 'kết thúc' của luồng đó, bạn có thể gọi stream.resume () để mở luồng dữ liệu.

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

Điều này có thể được gọi là "rút cạn" luồng. Trong trường hợp của chúng tôi, tất nhiên chúng tôi có một trình xử lý 'data'sự kiện, nhưng chúng tôi đã kiểm tra một boolean if (!ignoring) { ... }để nó sẽ không xử lý dữ liệu khi chúng tôi thoát luồng. ignoring = true; readable.resume();
joeytwiddle

5
Tất nhiên điều này giả định rằng một lúc nào đó luồng sẽ hoạt động 'end'. Không phải tất cả các luồng sẽ làm được điều đó! (Ví dụ: một luồng gửi ngày mỗi giây, mãi mãi.)
joeytwiddle

3

Đó là một câu hỏi cũ nhưng tôi cũng đang tìm kiếm câu trả lời và tìm ra câu trả lời tốt nhất để thực hiện. Cả hai endclosecác sự kiện đều được phát ra nên tôi nghĩ đây là giải pháp sạch nhất.

Thao tác này sẽ thực hiện thủ thuật trong nút 4.4. * (Phiên bản ổn định tại thời điểm viết bài):

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

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

Để có lời giải thích rất chi tiết, hãy xem: http://www.bennadel.com/blog/2692-you-have-to-explicit-end-streams- after-pipes-break-in-node-js.htm


2

Mã này ở đây sẽ thực hiện thủ thuật độc đáo:

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

writeStream.end () là cách đơn giản nhất để đóng một writeStream ...


Tại sao bạn đề cập đến .end () là một cách tốt nhất, nhưng sau đó mã của bạn sử dụng đóng và hủy và thậm chí không sử dụng kết thúc?
Lucas B

1
tôi đang đóng một luồng readStream trong ví dụ ... một writeStream - sử dụng.end
g00dnatur3
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.