我已经修改了您的程序以显示有关正在发生的事情的更多信息:
'use strict';
const fs = require('fs');
const readable = fs.createReadStream('read.mkv');
const writable = fs.createWriteStream('write.mkv');
readable.on('data', function(chunk){
var buffer = writable.write(chunk);
if(!buffer){ // ----> Always false! Why????
readable.pause();
}
console.log(buffer, chunk.length);
});
writable.on('drain', function(){
readable.resume();
console.log('drain');
});
输出:
$ node blah.js
false 65536
drain
false 65536
drain
false 65536
drain
true 8192
我还使用了一个不同大小的文件作为输入,所以我true
在输出的末尾有一个。如果我将read.mkv
' 的大小增加例如10000
字节,则最后一行将读取false 18192
.
正在发生的事情是,返回的每个块read()
都足够大,以至于它导致写入流超过其highWaterMark
默认值16384
(假设返回的流fs.createWriteStream
)。从输出中的数字可以看出,每个read()
(错误,每个'data'
事件)都会产生65536
除最后一个之外的字节。由于写入此数据量writable
会导致其超过其,因此该流建议在继续之前highWaterMark
等待。'drain'
因此,简单地说,您总是会看到false
发出的,因为readable
流在读取时会产生如此大的块。而且我希望不再看到任何日志表明传输已经完成。但是您确实需要注册.on('end')
并.on('error')
弄清楚这一点。
对于像这样的简单情况,最好只使用readable.pipe()
,例如:
readable.pipe(writable);
这将自动'drain'
为您处理。它甚至writable.end()
会适当地召唤你。
请注意,如果遇到读取或写入错误,pipe()
它将不会调用。writable.end()
如果您有一个长时间运行的进程需要对流错误具有弹性,您需要确保处理错误并关闭流以防止在程序运行时间足够长以达到文件描述符限制时处理泄漏。
什么false
意思
流使程序能够通过一次处理一个块而不是将其全部加载到内存中来扩展到大量数据。在数据最终被写出之前,流可以被组织成代表数据的各种转换的管道。当write()
返回时false
,它表示它已收到足够的数据以使其忙碌一段时间。如果您继续向它发送块,它将继续接受这些块。但是,其积压的数据将增长并开始消耗更多内存。如果您忽略此返回值并继续从一个非常大的源向其发送数据,您甚至可能导致程序耗尽其地址空间并崩溃或卡住。为了使您的代码具有可扩展性,您应该尊重false
返回并等待'drain
' ,就像您在代码中所做的那样。
但是,false
这并不意味着发生了任何不好的事情或有任何错误。事实上,在任何源流比目标流快的情况下,这种情况都会发生,并且是流 API 保持安全的方式。