当前位置: 首页 > 工具软件 > Stream.js > 使用案例 >

Node.js学习之Stream数据流(概念、作用及常用操作)

程志新
2023-12-01

概念

定义

数据流(Stream)是处理系统缓存的一种方式。操作系统采用数据块(chunk)的方式读取数据,每收到一次数据,就存入缓存。

Node应用程序有两种缓存的处理方式

(1)传统方式:等到所有数据接收完毕,一次性从缓存读取。这种方式先将数据全部读入内存,然后处理,如果遇到大文件,要花很长时间,才能进入数据处理的步骤。

(2)“数据流”方式每收到一块数据,就读取一块,即在数据还没有接收完成时就开始处理它。

这种方式每次只读入数据的一小块,每当系统读入一小块数据,就会触发一个事件,发出“新数据块”的信号。应用程序只要监听这个事件,就能掌握数据读取的进展,做出相应的处理,这样可以提高程序的性能。

流的分类

在node中,有四种类型的数据流:
(1)Readable:用于读操作( fs.createReadStream())

(2)Writeable:用于写操作(fs.createWriteStream())

(3)Duplex:用于读取和写入操作

(4)Transfrom:在读写过程中可以修改和变换的Duplex流

所有的Stream对象都是EventEmitter的实例。常用的事件有:

  • data:当有数据可读时触发
  • end:没有更多的数据刻度时触发
  • error:在接收和写入过程中发生错误时触发
  • finish:所有数据已被写入到底层系统时触发

流的作用**

Node.js中的流最大的作用是:读取大文件的过程中,不会一次性的读入到内存中。每次只会读取数据源的一个数据块(收到一个数据,就读取一块,即在数据还没有接收完就开始处理)。
然后后续过程中可以立即处理该数据块(数据处理完成后会进入垃圾回收机制),而不用等待所有的数据。

使用流的好处:

文件系统中读取文件的方式readfile()会把文件内容整个的读进以 Buffer格式存入到内存中,然后再写进返回对象,那么这样的效率非常低的,并且该文件如果是好几个G的大文件,那么内存会直接被卡死掉的,或者服务器直接会奔溃掉。

而使用流的createReadStream()方法就可以避免占用内存多的情况发生,在读取大文件的过程中,不会一次性的读入到内存中。每次只会读取数据源的一个数据块,这就是流的优点。

常用操作

文件输入流

文件输入流即通过流读取文件,所需步骤有:

(1)导入文件模块(有关文件模块,后续会有相关博客说明)

(2)创建一个输入流(从键盘输入或从文件读取)

(3)设置输入流的字符集(编码格式)

(4)绑定流事件

例如:

var fs=require('fs')
var data=''
//创建输入流
var readerStream=fs.createReadStream('input.txt')
readerStream.setEncoding('utf8')
readerStream.on('data',chunk={
	data+=chunk
})
readerStream.on('end',()=>{
	console.log(data)
})
readerStream.on('error',err=>{
	console.log(err.stack)
})
console.log('程序结束')

//程序结束
//文件内容

从这个过程我们也可以看出:这个过程是非阻塞的I/O

文件输出流

文件输出流即通过流向文件中写内容,所需步骤有:
(1)导入文件模块

(2)创建一个输出流(写入流)

(3)设置写入数据的字符集

(4)文件结束退出

(5)绑定流事件

例如:

var fs=require('fs')
//创建输出流
var writeStream=fs.createWriteStream('input.txt')
writeStream.write('Hello World','utf8')
writeStream.end()

writeStream.on('finish',()=>{
	console.log('写入完成')
})
writeStream.on('error',err=>{
	console.log(err.stack)
})
console.log('程序结束')
//程序结束
//写入完成

这也是一个非阻塞的I/O

管道流

管道提供了一个输出流到输入流的机制,通常我们用于从一个流中获取数据并将数据传递到另外一个流中。

所需步骤有:

(1)导入文件模块

(2)创建一个输入流,用于读取一个文件的内容

(3)创建一个输出流,用于向另一个文件写入内容

(4)创建管道流

例如:将input文件里面的内容传输到output文件中

var fs=require('fs')
var readerStream=fs.createReadStream('input.txt')
var writeStream=fs.createWriteStream('output.txt')
readerStream.pipe(writerStream)
console.log('程序结束')

链式流

链式是一个机制,一个流的输出连接到另一个流,并创建一个链多流操作(相当于接了好几根管道)。它通常用于管道的操作。

可以用于压缩文件以及文件解压,所需步骤:

(1)导入文件模块

(2)导入压缩解压缩模块

(3)压缩或解压缩文件

例如:压缩文件

var fs=require('fs')
var zlib=require('zlib')
fs.createReadStream('input.txt').pipe(zlib.createFzip()).pipe(fs.createWriteStream('input.zip')) 
console.log('程序结束')

文件解压缩

fs.createReadStream('input.zip').pipe(zlib.createGunzip()).pipe(fs.createWriteStream('input.txt'))
console.log('程序结束')

Buffer与Stream的联系

一个例子,我们在线看视频的时候。如果你的网络足够快,数据流(stream)就可以足够快,可以让buffer迅速填满然后发送和处理,然后处理另一个,再发送,再另一个,再发送,然后整个stream完成。

但是当你网络连接很慢,当处理完当前的数据后,你的播放器就会暂停,或出现”缓冲”(buffer)字样,意思是正在收集更多的数据,或者等待更多的数据到来,才能下一步处理。当buffer装满并处理好,播放器就会显示数据,也就是播放视频了。在播放当前内容的时候,更多的数据也会源源不断的传输、到达和在buffer等待。

如果播放器已经处理完或播放完前一个数据,buffer仍然没有填满,”buffering”(缓冲)字符就会再次出现,等待和收集更多的数据。

 类似资料: