Nodejs - 流媒体可读&可写的被误解了

时间:2016-02-16 16:23:26

标签: javascript node.js stream

我运行节点服务器并拥有以下代码:

var readable = fs.createReadStream(__dirname + '/greet.txt',
{encoding: 'utf8', highWaterMark: 332 * 1024});

greet.txt:

hello

我无法理解可读流和可写流; 在上面的代码中,我有一个可读的流,从greet.txt读取 - 块进入缓冲区,我可以看到二进制数据...问题是, 不应该有一个可写的流将数据发送到另一侧的缓冲区吗?二进制数据如何突然开始飞入我的缓冲区,它只是不清楚。

以下是可读和可写的组合:

var readable = fs.createReadStream(__dirname + '/greet.txt',
{encoding: 'utf8', highWaterMark: 332 * 1024});

var writeable = fs.createWriteStream(__dirname + '/greetcopy.txt');

readable.on('data', function(chunk){
writeable.write(chunk);
});

当一个块到达可读缓冲区,并通过一个事件被发送到可写流的缓冲区时,为了接收数据,可写流也不应该是可读的吗?一旦可写流的缓冲区从可读内容中获取信息并将其发送到greetcopy.txt文件(该文件为空),数据如何到达?

节点中可读写的概念过于简化,我很难抓住它们。感谢您的时间,我想了解幕后发生的事情......

2 个答案:

答案 0 :(得分:28)

Node.js流非常复杂且令人困惑。我花了很多时间试图理解它们,我将尝试在下面传达我的发现。

有5种类型,可读,可写,双工,转换和PassThrough。

好的,首先是简单的部分:可读和可写

<强>可读

  • 要将数据添加到可读流中,请使用.push()函数。当流完成时,你推(null)。
  • 结束时,可读的流会触发&#39; end&#39;事件。
  • 您可以通过聆听“可读”来读取可读流中的数据。事件然后执行&#39; read()&#39;直到它返回null。
  • 可读流有一个缓冲区,这意味着当你推送()&#39;到缓冲区,如果缓冲区已满,则push()将返回false。但是,您可以继续按下缓冲区并填充它,即使它已满。 &#39; highWaterMark&#39; (或缓冲区大小)确实是信息性的。
  • 可读流实现了一个_read()方法,用于从非流源中提取数据。但是,你不必使用它。您可以将此方法留空并使用前面描述的push方法。无论谁使用你的流都可以调用read(),它首先从内部缓冲区读取,然后在缓冲区为空时调用_read()。

<强>可写

  • 要将数据添加到可写流,请使用.write()函数。流完成后,使用.end()。
  • 当你调用.end()时,它不会立即结束流。它将使用process.nextTick()在下一个tick上结束流!这给我带来了许多种族疾病的心痛。
  • 可写流有缓冲区。如果缓冲区已满(highWaterMark),则在调用.write()时它将返回false。但是,如果需要,您可以继续写入并忽略此事件。否则,我认为有类似“排水”的东西。通知您可以继续写作的事件。
  • 可写流实现_write()方法,将数据发送到某些后端非流接收器。如果此方法返回false,则Writable流将开始缓冲数据,而不是再次调用_write()直到&#39; drain&#39;。

一起使用可读和可写的流

  • 您只能将一个可读流传输到一个可写流。这可能会让您感到困惑,因为您可能已经看到类似&#39; streamA.pipe(streamB).pipe(streamC)&#39; ...等语法。问题的实例是,此示例中唯一的可读流是streamA。唯一可写的流是streamC。 streamB(以及其间的任何其他流)是一种称为转换流的特殊流。
  • 关键点1 :您无法通过管道传输到可读流。一切都必须从可读流开始。
  • 关键点2 :您无法将可写流传输到其他任何内容。可写流是它结束的地方。数据必须通过_write方法()退出可写流。

让流相互传输的唯一方法是使用 transform 流。和我一起到目前为止?这是令人困惑的地方:Duplex,Transform和PassThrough

<强>双面

  • 双工流是可读写的流组合。管道双工流(或从双工流读取)时,它作为可读流操作。当您管道到双工流时,它将按可写流的确切方式运行。
  • 关键点1 :示例&#39; streamA.pipe(duplexB).pipe(streamC)&#39;表示从可读streamA的_read()方法读取数据并发送到duplexB的_write()方法。它不会流向streamC。它还意味着从duplexB的_read()方法读取的数据转到streamC。语法令人困惑,因为看起来数据是从streamA到streamC的一行。
  • 关键点2 :使用双工流时,无论是调用.push(null)还是.end()来结束流,都会让人感到非常困惑。你是否应该聆听“结束”这一点并非常令人困惑。或者&#39;完成&#39;事件。我仍然没有得到答案。调用end()是否隐式执行.push(null)?

这两个关键点使得使用Duplex流极为混乱。事实上,我想要一个完全如上所述工作的双向流,所以我创建了自己的here。我把它称为&#39; link-stream&#39;,它实际上并没有使用_read或_write方法。它从streamA获取数据并将其传输到streamC,反之亦然,在全双工模式下,你可以听完&#39;完成&#39;或者&#39;结束&#39;事件,没关系。它是真正的双向直通管道。

<强>变换

  • 转换流是双工流
  • 在变换流上调用write()会调用_write,它只调用_read()
  • 在变换流上调用this.push(...)会调用_read,调用_transform()
  • 基本上所有数据路径都会导致_transform()方法。您实现_transform方法。无论你如何使用流,它都可以作为可读或可写,并且数据总是到达同一个地方,_transform()方法
  • 调用_transform方法后,数据将被发送到管道传输到的任何可写流。

<强>直通

  • 这只是一个在_transform方法中什么都不做的转换流。

所以你有它。我真的希望Joyent人员能够清理Duplex并减少混乱,我真的希望他们添加双向PassThrough,所以我不必使用我上面描述的链路流方法

祝你好运!

答案 1 :(得分:0)

他们的概念相当简单,看起来你在这里有点困惑。流通常是Unix管道,允许您从源读取数据并将其传送到目标。实际上,每种类型的Stream都是一个 EventEmitter ,它实现了一些特定的方法,并根据这些方法将它们划分为不同类型的Streams,例如 Writable,Readable,Transform 等。

关于 可写流 ,请参阅官方文档here

  

可写流界面是 目标 的抽象,您将数据 写入

关于 可读流 ,请参阅官方文档here

  

可读流界面是您从 中读取 数据的抽象。换句话说,数据来自可读流。

因此,使用 Writable 流,您可以将数据写入目标,并使用可读流从源读取数据。我不相信它可以简化得更多,因为我将开始围绕相同的句子传播。

基于上述问题的答案

  为了接收数据,

可写流是否也不应该是可读的?

根本就没有,因为它没有“接收”事件中的数据,因为您看到使用的方法是.write(chunk);

来源

http://www.sitepoint.com/basics-node-js-streams/

http://maxogden.com/node-streams.html