Как закрыть читаемый поток (до конца)?


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 08

3
Хорошие новости. Node версии 8 предоставляетstream.destroy()
joeytwiddle

Разве ты не можешь позвонитьreadable.push(null) && readable.destroy();
Александр Миллс

Ответы:


40

Вызов input.close(). Этого нет в документации, но

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

явно выполняет свою работу :) На самом деле он делает что-то похожее на ваш isEnded.

РЕДАКТИРОВАТЬ 2015-апр-19 На основе комментариев ниже, а также для уточнения и обновления:

  • Это предложение является взломом и не задокументировано.
  • Хотя, если смотреть на текущий, lib/fs.jsон все еще работает> 1,5 года спустя.
  • Я согласен с приведенным ниже комментарием о destroy()предпочтительности звонка .
  • Как правильно указано ниже, это работает для fs ReadStreamss, а не для общегоReadable

Что касается общего решения: не похоже, что оно есть, по крайней мере, из моего понимания документации и беглого взгляда _stream_readable.js.

Мое предложение было бы перевести ваш читаемый поток в приостановленный режим, по крайней мере, предотвращая дальнейшую обработку в вашем исходном источнике данных. Не забудьте unpipe()удалить все dataпрослушиватели событий, чтобы pause()фактически приостанавливать работу, как указано в документации.


1
На самом деле я бы предпочел destroyвместо этого позвонить . По крайней мере, это то, что вызывается, если вы установите autoClose в true. Если посмотреть на исходный код (сегодня), различия минимальны ( destroyзвонки close), но это может измениться в будущем
Марсело Диниз

Не помню сейчас, но похоже :)
Nitzan Shaked

4
Нет close()объекта Readable, есть ли решение «никогда»? Мой обмен данными всегда неполный ...
CodeManX

Обновлено, чтобы прояснить, ответить на комментарии и предоставить предложение (бедняк) для общего случая. Хотя имеет смысл не принудительно реализовывать универсальный readableшаблон close(), а предоставить специфичный для класса способ сделать это (как в случае fsи, предположительно, в других классах Readable)
Ницан Шакед

Не вызовет ли пауза блокировку восходящего потока (отправителя) из-за противодавления или иным образом не приведет к увеличению буферов, пока они не превысят свои пределы? В идеале мы должны сказать отправителю, что он больше не нужен ...
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 октября 2012 г., фиксация ).

destroyФункция внутренне отметьте ReadStreamэкземпляр как уничтоженные и вызывает closeфункцию , чтобы освободить файл.

Вы можете прослушать событие закрытия, чтобы точно знать, когда файл закрыт. Событие окончания не будет срабатывать , если данные не будут полностью израсходованы.


Обратите внимание, что функции destroyclose) относятся к fs.ReadStream . Это не часть общего "интерфейса" stream.readable .


По крайней мере, в последней версии Node (другие не проверял) дескриптор файла закрывается автоматически . Тем не менее, я не проводил никаких тщательных тестов, чтобы гарантировать, что поток в конечном итоге сработает, errorесли он никогда не читается. Помимо этого, единственная утечка, о которой я бы беспокоился, - это обработчики событий - опять же, я не уверен на 100% в этом, но мы могли бы быть в порядке, потому что в 2010 году Евангелие Исаакса действительно говорит, что обработчики удаляется, когда эмиттеры gc'd: groups.google.com/d/msg/nodejs/pXbJVo0NtaY/BxUmF_jp9LkJ
mikermcneil

1
Если данных слишком мало, on('data') сработает только один раз, поэтому их не будет .close(), просто напомните кому-то еще.
bitfishxyz 02


12

Вы не можете. Нет задокументированного способа закрыть / выключить / прервать / уничтожить общий читаемый поток с Node 5.3.0. Это ограничение потоковой архитектуры узла.

Как объяснялось в других ответах здесь, существуют недокументированные хаки для конкретных реализаций Readable, предоставляемых Node, таких как fs.ReadStream . Однако это не общие решения для любого Readable.

Если кто-то сможет доказать, что я неправ, пожалуйста, сделайте это. Я хотел бы иметь возможность сделать то, что я говорю, невозможно, и был бы рад исправлению.

РЕДАКТИРОВАТЬ : Вот мой обходной путь: реализовать .destroy()для моего конвейера через сложную серию unpipe()вызовов. И после всей этой сложности он не во всех случаях работает должным образом .

РЕДАКТИРОВАТЬ : Node v8.0.0 добавил destroy()api для читаемых потоков .


1
Теперь там stream.pipelineутверждается, что он обрабатывает «ошибки пересылки и должным образом очищает и обеспечивает обратный вызов, когда конвейер завершен». Это помогает?
andrewdotn

11

В версии, 4.*.*добавление нулевого значения в поток вызовет EOFсигнал.

Из документации nodejs

Если передается значение, отличное от null, метод push () добавляет фрагмент данных в очередь для использования последующими потоковыми процессорами. Если передается null, он сигнализирует об окончании потока (EOF), после которого больше нельзя записывать данные.

Это сработало для меня после того, как я попробовал множество других вариантов на этой странице.


1
Работает на меня. Однако мне нужно было избегать вызова обратного вызова done () после нажатия null, чтобы получить ожидаемое поведение, а именно остановку всего потока.
Rich Apodaca

6

Этот модуль уничтожения предназначен для обеспечения уничтожения потока, обрабатывая различные API-интерфейсы и ошибки 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 () - это лучший способ, но тогда ваш код использует close и destroy и даже не использует end?
Lucas B

1
я закрываю readStream в примере ... writeStream - используйте.end
g00dnatur3
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.