第一个Stream例子
- 创建一个js文件:
``typescript const fs = require('fs') const stream = fs.createWriteStream('big_file.txt') //实际上可以理解为 这个stream可以往这个bigfile文件里面写东西 for(let i=0; i<100000; i++){ stream.write(
这是第${i}行内容,我们需要很多很多内容,要不停地写文件aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 回车\n`) }
stream.end() //别忘了关掉stream console.log(‘done’)
2. 运行该文件
![image.png](https://cdn.nlark.com/yuque/0/2020/png/851574/1606748934135-ad8bfd3e-d0b4-4e50-9fd8-56b6bdab5815.png#align=left&display=inline&height=46&margin=%5Bobject%20Object%5D&name=image.png&originHeight=46&originWidth=310&size=2314&status=done&style=none&width=310)<br />会创建一个big_file.txt (大小:100多MB)<br />![image.png](https://cdn.nlark.com/yuque/0/2020/png/851574/1606748944354-088a65a4-e87e-4ea6-99f9-45f94a363dd8.png#align=left&display=inline&height=497&margin=%5Bobject%20Object%5D&name=image.png&originHeight=497&originWidth=1035&size=92445&status=done&style=none&width=1035)
<a name="wSKiG"></a>
## 分析
- 打开流,多次往里面塞内容, 关闭流
每次塞是不会覆盖之前的内容的,每次都是分开的
- 看起来就是可以多次而已,没什么特别
- 但是最终得到一个100多MB的文件
<a name="g7oDy"></a>
# Stream - 流
<a name="wWfPp"></a>
## 图解:
![image.png](https://cdn.nlark.com/yuque/0/2020/png/851574/1606750077588-63b2411d-d33a-4791-98fa-675836d26cef.png#align=left&display=inline&height=530&margin=%5Bobject%20Object%5D&name=image.png&originHeight=530&originWidth=345&size=62219&status=done&style=none&width=345)
<a name="Z7Gbo"></a>
## 释义:
结合上面的代码看
- stream是水流,但默认没有水
- stream.write 可以让水流中有水(数据)
- 每次写的小数据叫做chunk(块)
- 产生数据的一段叫做source(源头)
- 得到数据的一段叫做sink(水池)
chunk 数据块<br />为什么不叫data呢<br />data一般表示完整的数据,chunk只是一块数据
<a name="wG1RJ"></a>
# 第二个例子
1. 创建2.js文件
```typescript
const fs = require('fs')
const http = require('http')
const server = http.createServer()
server.on('request',(request,response)=>{
//读上面创建的big_file.txt,放到response里面
fs.readFile('./big_file.txt',(error,data)=>{
if(error) throw error
response.end(data)
console.log('done')
})
})
server.listen(8888)
console.log(`8888`)
- 运行2.js
- 访问8888端口
分析
- 用任务管理器看看 Node.js 内存占用,大概130Mb
big_file 120Mb + node.js本身8Mb
- 内存占用太多了,这就是不用stream的缺点
一个用户的请求就用了100mb内存,那么10个用户,100个用户,服务器很难撑得住
- 用steam就可以解决这个问题,用stream改写这个例子
第三个例子
用stream改写第二个例子
const fs = require('fs')
const http = require('http')
const server = http.createServer()
server.on('request',(request,response)=>{
// 用流的形式去读bigFile
fs.createReadStream('./big_file.txt')
// 通过管道pipe将读完的bigFile传给response
stream.pipe(response)
stream.on('end', ()=>console.log('done'))
})
server.listen(8888)
console.log(`8888`)
运行该文件后,可以使用 $ curl http://localhost:8888 去请求这个bigFile
一边请求一边打开任务管理器看看占用的内存有多少
一般会稳定到30多MB,直到传完
分析
跟第二个例子对比:
节省了很多的内存,而且时间并不会非常长
- 查看node.js内存占用,基本不会高于40Mb
- 文件stream 和 response stream 通过管道相连
- 意思是 stream是一个流,response也是一个流,他们通过pipe管道连起来
- stream1 相当于 读文件的stream
- stream2 相当于 http发回给用户的response stream
- 1和2本来是没什么关系的,但是我们用了一个管道 把他们接起来了。那么stream1的数据都会自动地流向stream2。
释义
两个流可以用一个管道相连
stream1的末尾连接上stream2的开端
只要stream1有数据,就会流到stream2
常用代码
stream1.pipe(stream2)
链式操作
a.pipe(b).pipe(c)
//等价于
a.pipe(b)
b.pipe(c)
管道
管道可以通过事件实现
//stream1一有数据就塞给stream2
stream1.on('data',(chunk)=>{
stream2.write(chunk)
})
//stream1停了,就停掉stream2
stream1.on('end',()=>{
stream2.end()
})
- 监听stream1的on data事件,一旦触发,就把chunk数据写到stream2上
- 监听stream1的on end事件,stream1 end, stream2 也end
一般不这样写,因为写这么多代码,还不如写一个pipe
小结
- stream可以使你的内存降得非常低,从100多mb降到30多mb
比如说你有一个3g的文件,nodejs对于内存的大小使用是有限制的,3g的文件根本读不到内存里的,但是如果使用流,可以一点一点地读。当然你也可以控制每次读多少
- 管道
可以将两个流连起来,连起来之后就可以实现数据在不同的地方的转换
比如上面的例子:从文件转换到网络