手机版

Nodejs学习笔记的流模块

时间:2021-10-15 来源:互联网 编辑:宝哥软件园 浏览:

一、开篇分析。

流是一个抽象接口,由Node中的许多对象实现。例如,对HTTP服务器的请求是一个流,stdout也是一个流。流是可读的、可写的或两者都有。

最早接触Stream是从早期的unix开始的,几十年的实践证明,Stream可以轻松开发一些庞大的系统。

在unix中,流是由“|”实现的。在节点中,作为内置的流模块,使用了许多核心模块和三方模块。

和unix一样,节点流的主要操作是。管道(),用户可以使用背压机制来控制读写之间的平衡。

Stream可以为开发者提供一个可重用的统一接口,通过抽象的stream接口控制Streams之间的读写平衡。

TCP连接既是可读流也是可写流,而Http连接则不同。http请求对象是可读的流,而http响应对象是可写的流。

默认情况下,流的传输过程是以缓冲区的形式传输的,除非您为他设置了其他编码形式。这里有一个例子:

复制代码如下: var http=require(' http ');var server=http . createserver(function(req,res){ res.writeHeader(200,{ ' Content-Type ' : ' text/plain ' });“你好,大熊!”) ;}) ;server . listen(8888);console.log('在端口8888上运行的http服务器.') ;

运行后会出现乱码字符,因为没有设置指定的字符集,如“utf-8”。

只需修改它:

复制代码如下: var http=require(' http ');var server=http . createserver(function(req,res){ res.writeHeader(200,{ 'Content-Type' : '纯文本/纯文本;charset=utf-8 '/add charset=utf-8 });“你好,大熊!”) ;}) ;server . listen(8888);console.log('在端口8888上运行的http服务器.') ;

运行结果:

为什么Streamnode中的I/O是异步的,所以读写磁盘和网络需要通过回调函数读取数据。以下是文件下载示例的代码:

复制代码如下: var http=require(' http ');var fs=require(' fs ');var server=http . createserver(function(req,RES){ fs . read file(_ dirname '/data . txt '),function (err,data){ RES . end(data);}) ;}) ;server . listen(8888);

代码可以实现所需的功能,但是服务需要在发送文件数据之前将整个文件数据缓存到内存中。如果' data.txt '文件很大,并发量很大,会浪费很多内存。由于用户需要等到整个文件都缓存在内存中后才能接受文件数据,用户体验相当糟糕。幸运的是(req,res)两个参数都是Stream,所以我们可以用fs.createReadStream()替换fs.readFile()。如下所示:

复制代码如下: var http=require(' http ');var fs=require(' fs ');var server=http . createserver(function(req,RES){ var stream=fs . createreadstream(_ _ dirname '/data . txt ');蒸汽管道;}) ;server . listen(8888);

那个。pipe()方法监控fs.createReadStream()的“data”和“end”事件,使得“data.txt”文件不需要缓存整个文件,在客户端连接完成后可以立即向客户端发送一个数据块。使用的另一个优点。pipe()是它可以解决客户端延迟很大时读写不平衡的问题。

有五种基本流:可读、可写、转换、双工和“经典”。(具体使用请自行参考api)

二、实例介绍。

当需要处理的数据不能一次加载到内存中,或者同时读取和处理效率更高时,我们就需要使用数据流。在NodeJS中,各种流被用来提供对数据流的操作。

以大文件复制程序为例,我们可以为数据源创建一个只读数据流。示例如下:

复制代码如下:varrs=fs.createreadstream(路径名);rs.on('data ',function(chunk){ DosMeming(chunk);//具体细节请随意播放});rs.on('end ',function(){ clean up();}) ;

代码数据事件会不断被触发,不管doSomething函数能否处理。为了解决这个问题,可以对代码进行如下修改。

复制代码如下: varrs=fs . create readstream(src);rs.on('data ',function(chunk){ RS . pause();dosame ing(chunk,function(){ RS . resume();}) ;}) ;rs.on('end ',function(){ clean up();}) ;doSomething函数增加了一个回调函数,这样我们可以在处理数据之前暂停数据读取,在处理数据之后继续读取数据。

此外,我们还可以为数据目标创建只写数据流,如下所示:

复制代码如下: varrs=fs . create readstream(src);var ws=fs . CreateWriteStream(dst);rs.on('data ',function(chunk){ ws . write(chunk);}) ;rs.on('end ',function(){ ws . end();}) ;

将“doSomething”改为“只写数据流”后,上面的代码看起来像是一个文件复制程序。然而,上述代码存在上述问题。如果写速度跟不上读速度,只写数据流中的缓存就会爆炸。根据。write方法,我们可以判断传入的数据是写入目标还是临时放在缓存中,并根据drain事件,判断只写数据流何时已经将缓存中的数据写入目标,从而可以传入下一个要写入的数据。所以代码如下:

复制代码如下: varrs=fs . create readstream(src);var ws=fs . CreateWriteStream(dst);rs.on('data ',function(chunk){ if(ws . write(chunk)==false){ RS . pause();}}) ;rs.on('end ',function(){ ws . end();});ws.on('drain ',function(){ RS . resume();}) ;

最终实现从只读数据流到只写数据流的数据传输,并包含防爆仓库控制。因为有很多使用场景,比如上面的大文件复制程序,NodeJS直接提供了。管道方法来实现,其内部实现类似于上面的代码。

以下是复制文件的更完整的过程:

复制代码如下:varfs=require ('fs '),path=require ('path '),out=process.stdoutvar file path='/bb/big bear . mkv ';var readStream=fs . createreadstream(文件路径);var WriteStream=fs . CreateWriteStream(' file . mkv ');var stat=fs . stat sync(file path);var totalSize=stat.sizevar passedLength=0;var lastSize=0;var startTime=date . now();readStream.on('data ',function(chunk){ passedLength=chunk . length;if(WriteStream . write(chunk)==false){ ReadStream . pause();}});readStream.on('end ',function(){ writestream . end();});writeStream.on('drain ',function(){ readstream . resume();});setTimeout(函数show(){ var percent=math . ceil((passedLength/total size)* 100);var size=math . ceil(passedLength/1000000);var diff=size-LastSize;lastSize=大小;out . clearline();out . cursorto(0);Out.write('完成'大小' MB,'百分比' %,速度:' diff * 2 ' MB/s ');if(passedLength total size){ setTimeout(show,500);} else { var end time=date . now();console . log();Console.log('共享时':(endTime-startTime)/1000 '秒');}}, 500);

您可以将上面的代码保存为“copy.js”并进行测试。我们添加了一个递归setTimeout(或者直接使用setInterval)作为旁观者。

每500ms观察一次完成进度,并将完成的大小、百分比和复制速度写入控制台。复制完成后,计算总运行时间。

第三,总结。

(1)了解Stream的概念。

(2)熟练使用相关流的api。

(3)注意细节的控制,比如大文件的拷贝以“组块数据”的形式碎片化。

(4)管道的使用。

(5)重新强调一个概念:TCP连接既是可读流,也是可写流,而Http连接则不同。http请求对象是可读的流,而http响应对象是可写的流。

版权声明:Nodejs学习笔记的流模块是由宝哥软件园云端程序自动收集整理而来。如果本文侵犯了你的权益,请联系本站底部QQ或者邮箱删除。