当前位置: 首页 > 后端技术 > Node.js

浅谈node.js中的流(stream)

时间:2023-04-03 12:09:24 Node.js

前言什么是stream?看字面意思,我们可能会想到生活中的水流,电流。但流既不是水也不是电,它只是描述水和电的流动;所以流程是抽象的。在node.js中,stream是一个抽象接口。它不关心文件的内容。它只关心数据是否从文件中读取,以及读取数据后的处理。接下来看:1.流的概念。Stream是一组有序的,有起点和终点的字节数据传输方式,不关心文件的整体内容,只关心数据是否从文件中读取;而读取数据后的处理流程是一个抽象的接口,由node.js中的很多对象实现。比如HTTP服务器的请求和响应对象都是流,TCP服务器中的socket也是流。看看官网上的介绍:说“所有的流都是EventEmitter的实例”,所以流继承了EventEmitter类。再看流的类型:2.流的类型Reacable-可读流(例如fs.createReadStream())Writrable-可写流(例如fs.createWriteStream())Duplex-可读可写流(例如fs.createWriteStream())例如,net.socket)Transform-Duplexstream,可以在读写过程中修改和转换数据(如zlib.createDuplex())3.流的数据模式流中的数据有两种模式,二进制模式和对象模式。二进制模式,每个块是一个缓冲区或字符串对象。在对象模式下,流的内部处理是一系列普通的对象。注意:所有使用Node.jsAPI创建的流对象只能对字符串和Buffer对象进行操作。但是,借助一些第三方流实现,您仍然可以处理其他类型的JavaScript值(null除外,它在流处理中具有特殊含义)。据说这些流以“对象模式”工作。创建流实例时,可以通过objectMode选项将流实例切换为对象模式。尝试将已存在的流切换到对象模式是不安全的。说了这么多,现在开始写流:4.可读流(createReadStream)4.1创建可读流下面是流程:首先可读流会打开文件,触发open事件然后开始读取,触发数据事件疯狂然后读取后,触发结束事件。最后因为autoClose设置为true,文件自动关闭,close事件被触发。可以看到数据事件不断被触发。想读书想停下来怎么办?这里需要说一下可读流的两种模式:4.2可读流的两种模式可读流实际上工作在以下两种模式之一:流动和暂停。在流动模式下,可读流自动从系统底层开始读取??数据,并通过EventEmitter接口的事件尽快将数据提供给应用程序。在暂停模式下,必须显式调用stream.read()方法才能从流中读取数据段。初始工作模式为paused的Readable流可以通过以下三种方式切换到flowing模式:1.监听'data'事件2.调用stream.resume()方法3.调用stream.pipe()方法向Writable发送数据注意:如果Readable切换为流动模式,并且没有消费者处理流中的数据,则数据将丢失。比如调用了readable.resume()方法但是没有监听到'data'事件,或者取消了'data'事件监听,就可能出现这种情况。在暂停模式下,必须显式调用stream.read()方法才能从流中读取数据片段。在可读流'readable'事件中,我们必须调用stream.read()方法。4.3可读流'readable'事件这里需要了解三点:首先创建一个1.txt1。当我刚刚创建一个流时,它会先填充缓冲区,等待你自己消费。2.当你的消费小于最高水位线时3.如果当前缓冲区清空,会再次触发readable事件。4.4可读原理图用Readable创建可读对象后,得到一个可读流。如果实现了_read方法,则流连接到基础数据源。stream通过调用_read向底层请求数据,底层调用stream的push方法传递需要的数据。当readable连接到数据源后,下游可以调用readable.read(n)向stream请求数据,监听readable的data事件接收接收到的数据。5.可写流(createWriteStream)5.1创建可写流5.1.1write方法5.1.2end方法表示没有数据写入Writablenext通过传入可选的chunk和encoding参数,可以在Write之前关闭流一条数据。如果传入可选的回调函数,它将作为'finish'事件的回调函数。5.1.3drain方法drain事件的触发条件必须满足两个条件:1.当前buffer已满,不能再Write2.当buffer已满清空时会触发drain事件。6、我们在开发中可能会遇到的管道方法(pipeline)。从可读流读取的数据需要放到可写流中写入文件,这时就可以使用pipe方法6.1pipe的原理pipe方法的原理很简单,只读一点,写一个一点,然后把代码letfs=require('fs');letws=fs.createWriteStream('./2.txt');letrs=fs.createReadStream('./1.txt');rs.on('data',data=>{varflag=ws.write(data);如果(!flag)rs.pause();});ws.on('drain',()=>{rs.resume();});rs.on('end',()=>{ws.end();});6.2管道的用法letfrom=fs.createReadStream('./1.txt');letto=fs.createWriteStream('./2.txt');from.pipe(to);6.3unpipe方法readable.unpipe()方法将之前通过stream.pipe()方法绑定的stream分离出来letfs=require('fs');letfrom=fs.createReadStream('./1.txt');letto=fs.createWriteStream('./2.txt');从.管道(到);setTimeout(()=>{console.log('关闭写入2.txt');from.unpipe(可写);console.log('手动关闭可写流');to.end();},1000);7.我们可以引入stream模块来自定义stream,如果要实现任何stream,都继承这个stream7.1自定义可读stream,可以直接把数据推送出来使用。当推送一个空对象时,意味着我们想要发出信号表示该流没有更多数据。7.2自定义可写流为了实现可写流,我们需要使用流模块中的Writable构造函数。我们只是将一些选项传递给Writable构造函数并创建一个对象。唯一需要的选项是写入函数,它揭示了数据块要写入的位置。7.3双工流(duplex)的实现通过双工流,我们可以同时对同一个对象实现可读和可写,就好像同时继承了这两个接口一样。重要的是双工流的读写操作是完全独立的。net中的Socket是双工双工流8.结语说到这里,我想大家应该对node.js中的流有了一个大概的了解。之前说过node里面的flow还是很重要的。http中的请求和响应都是流的。下一篇我会写一个简单的readStream和writeStream的实现。本人水平有限,不对的地方还望指出。