什么是流?流是一种抽象的数据结构。想象水流,当在水管中流动时
nodejs
的流接口:
Stream 有四种流类型:
- Readable - 可读操作。
- Writable - 可写操作。
- Duplex - 可读可写操作.
- Transform - 操作被写入数据,然后读出结果。
所有的 Stream 对象都是 EventEmitter 的实例。常用的事件有:
- data - 当有数据可读时触发。
- end - 没有更多的数据可读时触发。
- error - 在接收和写入过程中发生错误时触发。
- finish - 所有数据已被写入到底层系统时触发。
可读流 createReadStream
可读流是生产数据用来供程序消费的流。常见的数据生产方式有读取磁盘文件、读取网络请求内容等。
const fs = require("fs");
const path=require("path");
let rs = fs.createReadStream(path.resolve(__dirname, "./read.txt"))
// 保存读取的数据
const data=[];
// 读取数据
rs.on("data",(chunk)=>data.push(chunk));
// 读完收工
rs.on("end", () => console.log(Buffer.concat(data).toString()))
// 出错
rs.on("error", (err) => console.log(`${err.path}文件路径错误或损坏`));
可写流 createWriteStream
可写流是对数据流向设备的抽象,通常将可读流读取到的数据通过可写流写入。
const fs = require("fs");
const path=require("path");
let ws = fs.createWriteStream(path.resolve(__dirname, "./write.txt"))
ws.write("使用Stream写入文本数a据...\n");
ws.write(Buffer.from("使用Stream写入二进制数据...\n"))
ws.write("END.");
ws.end();
ws.on("finish", () =>console.log("写入完成"));
管道流
管道提供了一个输出流到输入流的机制。通常我们用于从一个流中获取数据并将数据传递到另外一个流中。
var fs = require("fs");
// 创建一个可读流
var readerStream = fs.createReadStream('input.txt');
// 创建一个可写流
var writerStream = fs.createWriteStream('output.txt');
// 管道读写操作
// 读取 input.txt 文件内容,并将内容写入到 output.txt 文件中
readerStream.pipe(writerStream);
利用管道流优化大文件在网络中的传输
通过读取文件到内容,在完成后再响应给客户端,遇到大文件消耗内存且等待时间过长
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);
利用http请求中request
和response
可读流和可写流的特性,通过管道传输
const fs = require('fs');
const server = require('http').createServer();
server.on('request', (req, res) => {
const src = fs.createReadStream('./big.file');
src.pipe(res);
});
server.listen(8000);
读流写流和双工流
所有可以读取数据的流都继承自stream.Readable,所有可以写入的流都继承自stream.Writable。
Readable
const { Readable } = require("stream")
let arr=["a","b","c","d"];
class MyReadStream extends Readable {
_read() {
if (arr.length) return this.push(arr.splice(0, 1)[0])
// 返回null表示读完
return this.push(null)
}
}
let rs = new MyReadStream()
let data = []
rs.on("data", (chunk) => data.push(chunk))
rs.on("end", () => {
let result = Buffer.concat(data).toString()
console.log(result)
})
Writable
const fs = require("fs")
const { resolve } = require("path")
const { Writable } = require("stream")
class MyWriteStream extends Writable {
_write(chunk, encoding, callback) {
fs.appendFile(resolve(__dirname, "./write.txt"), chunk, () => {
// 3 秒写入一次
// callback 相当于 clearBuffer() 清除缓存的读取数据
setTimeout(callback, 3000)
})
}
}
let ws = new MyWriteStream()
ws.write("新")
ws.write("增")
ws.write("加")
ws.write("的")
ws.write("内容")
Duplex 双工流
双工流既能读又能写
const { Duplex } = require("stream")
class MyDuplex extends Duplex {
_read() {
console.log("read")
}
_write(chunk, encoding, callback) {
console.log(chunk)
callback()
}
}
let duplex = new MyDuplex()
duplex.on("data", (chunk) => console.log("on data"))
duplex.write("1")
duplex.write("2")
Transform
_transform
能够截取到写入的流内容
const { Transform } = require("stream")
class MyTransform extends Transform{
_transform(chunk,encoding,callback){
this.push("test"); // 多push个内容
this.push(chunk);
// this.push(null);
callback();
}
}
let myTransform = new MyTransform();
myTransform.on("data",(chunk)=>{
console.log(chunk.toString());
})
myTransform.write("这是写入的内容");
修改输入控制台的输出内容:
const { Transform } = require("stream")
class MyTransform extends Transform{
_transform(chunk,encoding,callback){
// 对输入内容进行大小写转换
this.push(chunk.toString().toUpperCase());
this.push("\n\r")
callback()
}
}
let myTransform = new MyTransform();
process.stdin.pipe(myTransform).pipe(process.stdout);
参考链接
Node.js Streams: Everything you need to know
Node.js Stream(流)
廖雪峰JavaScript教程
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。