简介
流是数据的集合 —— 就像数组或字符串一样。区别在于流中的数据可能不会立刻就全部可用,并且你无需一次性地把这些数据全部放入内存。这使得流在操作大量数据或是数据从外部来源逐段发送过来的时候变得非常有用。
一个小例子
创建一个大文件
先写一个makeFile.js
脚本来创建一个大文件
const fs = require('fs') const file = fs.createWriteStream('./big.file') for(let i = 0;i<1e6;i++){ file.write('i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end i love front-end \n') } file.end()
然后开启服务,创建文件server.js
const fs = require('fs'); const server = require('http').createServer(); server.on('request', (req, res) => { fs.readFile('./big.file', (err, data) => { if (err) throw err; res.end(data); }); }); server.listen(8000,()=>{ console.log(process.pid) //5400 });
这边先看一下程序所占用的内存,是没有任何问题的
然后我们开始访问服务,可以看到所占用的内存疯狂增长
因为node一次性把内容读进了内存中。
使用流
我们再来使用node中的流来处理这个问题,稍稍改一下server.js
const fs = require('fs'); const server = require('http').createServer(); server.on('request', (req, res) => { let rs = fs.createReadStream('./big.file') rs.on('data', data => { res.end(data) }) }); server.listen(8000, () => { console.log(process.pid) });
这个时候再去访问服务,可以看到内存虽然也是增长了,但是没有刚才那么夸张,只是增长了一点点而已.
由此我们可以看出流在处理文件时候的威力之大!
流的API
可读流
创建可读流
var rs = fs.createReadStream(path,[options])
path
为读取文件的路径options
常用参数
flags
打开文件要做的操作,默认为'r'
encoding
默认为nullstart
开始读取的索引位置end
结束读取的索引位置(包括结束位置)highWaterMark
读取缓存区默认的大小64kb
监听data事件
流切换到流动模式,数据会被尽可能快的读出
rs.on('data', function (data) { console.log(data); });
监听end事件
该事件会在读完数据后被触发
rs.on('end', function () { console.log('读取完成'); });
监听error事件
rs.on('error', function (err) { console.log(err); });
暂停和恢复触发data
通过pause()
方法和resume()
方法
rs.on('data', function (data) { rs.pause(); console.log(data); }); setTimeout(function () { rs.resume(); },2000);
可写流
创建可写流
var ws = fs.createWriteStream(path,[options]);
path
写入的文件路径options
常用参数
flags
打开文件要做的操作,默认为'w'encoding
默认为utf8highWaterMark
写入缓存区的默认大小16kb
write方法
ws.write(chunk,[encoding],[callback]);
- chunk写入的数据buffer/string
- encoding编码格式chunk为字符串时有用,可选
- callback 写入成功后的回调
返回值为布尔值,系统缓存区满时为false,未满时为true
end方法
ws.end(chunk,[encoding],[callback]);
表明接下来没有数据要被写入 Writable 通过传入可选的 chunk 和 encoding 参数,可以在关闭流之前再写入一段数据 如果传入了可选的 callback 函数,它将作为 'finish' 事件的回调函数
drain方法
当一个流不处在 drain
的状态, 对 write() 的调用会缓存数据,并且返回 false。一旦所有当前所有缓存的数据块都排空了,那么 'drain' 事件就会被触发
finish方法
在调用了stream.end()
方法,且缓冲区数据都已经传给底层系统之后, finish
事件将被触发
var writer = fs.createWriteStream('./2.txt'); for (let i = 0; i < 100; i++) { writer.write(`hello, ${i}!\n`); } writer.end('结束\n'); writer.on('finish', () => { console.error('所有的写入已经完成!'); });
pipe方法
pipe
方法的意思是管道,可以控制速率。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
var fs = require("fs"); // 创建一个可读流 var readerStream = fs.createReadStream('input.txt'); // 创建一个可写流 var writerStream = fs.createWriteStream('output.txt'); // 管道读写操作 // 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中 readerStream.pipe(writerStream);
- 会监听rs的on('data'),将读取到的内容调用ws.write方法
- 调用写的方法会返回一个boolean类型
- 如果返回了false就调用rs.pause()暂停读取
- 等待可写流写入完毕后 on('drain')在恢复读取 pipe方法的使用