前言
🤡在前端我们一般很少接触到流的概念,如果你接触过RxJS
的话,你可能会对这个概念比较熟悉,但是在服务器开发流是一个非常重要的概念,但是相对于其他的概念来讲比较难理解,那么就让我们一次性来搞定这个疑难杂症吧~
一.认识Stream
😀什么是Stream(小溪,小河,在编程中通常翻译为流),提到流我们的第一反应应该是流水,流动的小溪,源源不断的流动,其实程序之中的流这样的概念,我们可以想象当我们从一个文件中读取数据时,文件的二进制(字节)数据会源源不断的被读取到我们的程序中,是连续字节的一种表现形式和抽象概念,流应该是可读的也是可写的。
🐻当然学到这里其实就有疑问了,为什么文件的读取我们前面都是通过readFile
和writeFile
方式来读取和操作文件,为什么还需要流哪?
- 直接读取的文件的方式虽然简单,但是无法控制一些细节。
- 比如从什么位置读,读到什么位置,一次性读取多少个字节。
- 读到某个位置的时候,暂停读取,在某个时刻恢复读取等等。
- 或者文件非常的大,比如一个视频文件,一次性全部读取是非常不合适的。
🦝实际上在Node中很多对象都是基于流来实现的
- http模块的
Request
和Response
对象
😈Node.js中有四种基本流类型
Writable
:可以向其写入数据的流(例如:fs.createWriteStream())Readable
:可以从中读取数据的流(例如:fs.createReadStream())Duplex
:同时为Readable和Writeable,又被称为双工流(例如:net.Socket)Transform
:Duplex可以在写入和读取数据时修改或者转换数据的流(例如:zlib.createDeflate())
二.可读流的基本使用
🥺我们知道我们之前读取文件一般使用fs模块中的数据进行去读,这样读取的方式会有很多的问题,主要缺点如下
- 没有办法精准的控制从什么地方读取,读取到某个位置。
- 读取到某一个位置的,暂停读取,恢复读取。
- 文件非常大的时候,多次读取。
🐳Readable
可读流:当我们在读取内容的时候还可以暂停readAble.pause()
js
const fs = require('fs')
const readAble = fs.createReadStream('./aaa.txt', {
start: 3, // 从什么地方开始
end: 8, // 从什么地方结束,包括end结束位置
highWaterMark: 3 // 每次读完三个输出一次,如果不设置则默认64kb
})
readAble.on('data', (data) => {
console.log(data.toString())
readAble.pause() // 暂停一次
setTimeout(() => {
readAble.resume() // 两秒钟后恢复读取
}, 2000)
})
🐻可读流的其他事件
不知道你注意到了没有,在上边我们使用了on
进行监听了事件,不知道你思考了没有为什么能够通过on
进行监听,这种监听方式不应该是events中的用法吗?是的,确实是events中的用法,在可读流之所以能够因为在可读流中继承了了events所以可以使用它所有的方法,基于这个知识,我们可以看下可读流的其他方法
- 通过流将文件打开
js
readStream.on('open',(fd)=>{
console.log("通过流将文件打开")
})
- 已经读取到了文件末尾
js
readStream.on('end',()=>{
console.log("已经读取到文件的末尾")
})
- 监听文件什么时候读取结束
js
readStream.on('close',()=>{
console.log("文件已经被关闭")
})
三.可写流的基本使用
我们在上述中学习了可读取流,我们知道在fs中我们也可以对文件进行写入,但是像文件读取一样会有文件的一次性读取的问题,所以我们在写入文件的时候一般也会使用写入流来完成。
👹创建一个可写流
js
const fs = require("fs")
const writeAble = fs.createWriteStream('./ccc.txt', {
flag: 'a+',
start: 5,
})
writeAble.write("HelloWorld") //能够被多次写入调用
🥺写入完成:当写入完成的时候,需要手动去掉close方法
js
writeStream.close()
🤡end方法,将最后的内容写入文件中,并且可以关闭文件,所以一般我们很少调用close而是调用end。
js
writeStream.end("哈哈哈哈")
四.pip方法的使用
😊在学习pip
之前我们首先来写一个小案例,对一个文件通过代码进行拷贝,方法不限
- 一次性进行读取和写入操作
js
const fs = require("fs")
fs.readFile("./ccc.txt", (err, data) => {
if (err) return
fs.writeFile("./ddd.txt", data, () => {
console.log("文件写入成功")
})
})
- 通过可读流和可写流
js
const fs = require("fs")
const readStream = fs.createReadStream("./aaa.txt")
const writeStream = fs.createWriteStream("./foo.txt")
readStream.on("data", (data) => {
writeStream.write(data)
})
writeStream.on("end", () => {
writeStream.close()
})
- 在可读流和可写流之间建立一个管道
pip
直接将读取的内容进行写入,这里使用的pip就是管道的意思,其作用就是将可读流的内容直接写入到可写流的文件中。
js
const fs = require("fs")
const readAble = fs.createReadStream("./aaa.txt")
const writeAble = fs.createWriteStream("./foocopy.txt")
readAble.pipe(writeAble)
五.总结
🦝我们在本小结学习了流
的概念,在日常生活中我们提到这个词我们可能会想到小溪,但是在编程中流往往指的是数据流,我们在之前对文件进行操作的时候,我们会使用fs
进行一次性的读取或者写入,但是这样的缺点有很多,包括不能进行初始结束位置的控制等等内容,因此我们使用了流来解决这个问题,我们通过可读流,可写流来积进行文件的读取和使用,并且可以通过方法对文件的读写进行监听,除此之外我们还使用了pip
方法,来直接通过管道将读取的流直接写入可写的文件中,当然这些内容仅仅是Node中的冰山一角,但是也是我们全栈之路的毕竟之路~