1
什么是流?流是一种抽象的数据结构。想象水流,当在水管中流动时

nodejs 的流接口:
image.png

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请求中requestresponse可读流和可写流的特性,通过管道传输

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。

image.png

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教程


chenwl
117 声望5 粉丝

平坦的路面上曲折前行