likes
comments
collection
share

模拟可读流,可写流实现

作者站长头像
站长
· 阅读数 18

可读流模拟实现

const fs = require("fs");
const EventEmitter = require("events");

class MyFileReadStream extends EventEmitter {
  constructor(path, options = {}) {
    super();
    this.path = path;
    this.flag = options.flags || "r";
    this.mode = options.mode || 438;
    this.autoClose = options.autoClose || true;
    this.start = options.start || 0;
    this.end = options.end;
    this.highWaterMark = options.highWaterMark || 64 * 1024;

    this.readOffset = 0;

    this.open();
    this.on("newListener", (type) => {
      // 判断监听类型
      console.log(type);
      if (type === "data") {
        this.read();
      }
    });
  }

  open() {
    //   原生open方法打开指定位置文件
    fs.open(this.path, this.flags, this.mode, (err, fd) => {
      if (err) {
        this.emit("error", err);
      } else {
        this.fd = fd;
        this.emit("open", fd); //  发送文件标识符
      }
    });
  }

  read() {
    // 这里无法直接获取fd,这里监听一下open时间,控制执行顺序
    if (typeof this.fd !== "number") {
      return this.once("open", this.read);
    }
    let buf = Buffer.alloc(this.highWaterMark);
    let howMuchToRead;
    // 用户设置了end值
    howMuchToRead = this.end
      ? Math.min(this.end - this.readOffset + 1, this.highWaterMark)
      : this.highWaterMark;
    fs.read(
      this.fd,
      buf,
      0,
      howMuchToRead,
      this.readOffset,
      (err, readBytes) => {
        if (readBytes) {
          this.readOffset += readBytes;
          this.emit("data", buf.slice(0, readBytes)); // 返回数据
          this.read(); // 继续读
        } else {
          this.emit("end");
          this.close();
        }
      }
    );
  }

  close() {
    fs.close(this.fd, () => {
      this.emit("close");
    });
  }

  // pipe简化了操作
  pipe(ws) {
    this.on("data", (data) => {
      let flag = ws.write(data);
      if (!flag) {
        this.pause();
      }
    });
    ws.on("drain", () => {
      this.resume();
    });
  }
}

let rs = new MyFileReadStream("test.txt", {
  end: 7,
  highWaterMark: 3,
});

//事件触发测试
// rs.on("open", (fd) => {
//   console.log("open", fd);
// });
// rs.on("error", (err) => {
//   console.log(err);
// });

rs.on("data", (chunk) => {
  console.log(chunk);
});

// rs.on("end", () => {
//   console.log("end");
// });

rs.on("close", () => {
  console.log("close");
});

可写流模拟实现

const fs = require("fs");
const EventEmitter = require("events");
const { Queue } = require("./linkedList"); // 队列

class MyWriteStream extends EventEmitter {
  constructor(path, options = {}) {
    super();
    this.path = path;
    this.flags = options.flags || "w";
    this.mode = options.mode || 438;
    this.autoClose = options.autoClose || true;
    this.start = options.start || 0;
    this.encoding = options.encoding || "utf8";
    this.highWaterMark = options.highWaterMark || 16 * 1024;

    this.open();

    this.writeOffset = this.start;
    this.writting = false; // 是否正在写入标识符
    this.writLen = 0;
    this.needDrain = false;
    this.cache = new Queue();
  }

  open() {
    // 原生fs.open
    fs.open(this.path, this.flags, (err, fd) => {
      if (err) {
        this.emit("error", err);
      }
      // 正常打开文件
      this.fd = fd;
      this.emit("open", fd);
    });
  }

  write(chunk, encoding, cb) {
    chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk); // 只考虑字符串或者Buffer两种类型
    this.writLen += chunk.length;
    let flag = this.writLen < this.highWaterMark;
    this.needDrain = !flag;
    // 判断是否正在写入
    if (this.writting) {
      // 正在写入,内容需要排队
      this.cache.enQueue({ chunk, encoding, cb });
    } else {
      this.writting = true;
      // 当前不是正在写入那么就执行写入
      this._write(chunk, encoding, () => {
        cb();
        //   清空排队内容
        this._clearBuffer();
      });
    }
    return flag;
  }

  _write(chunk, encoding, cb) {
    // 写入操作,与可读流有同样问题,需要在open之后在拿fd操作
    if (typeof this.fd !== "number") {
      return this.once("open", () => {
        return this._write(chunk, encoding, cb);
      });
    }
    fs.write(
      this.fd,
      chunk,
      this.start,
      chunk.length,
      this.writeOffset,
      (err, written) => {
        this.writeOffset += written;
        this.writLen -= written;
        cb && cb();
      }
    );
  }

  _clearBuffer() {
    let data = this.cache.deQueue();
    if (data) {
      this._write(data.element.chunk, data.element.encoding, () => {
        data.element.cb && data.element.cb();
        this._clearBuffer();
      });
    } else {
      if (this.needDrain) {
        this.needDrain = false;
        this.emit("drain");
      }
    }
  }
}

const ws = new MyWriteStream("test.txt", { highWaterMark: 1 });

ws.open("open", (fd) => {
  console.log("open===>", fd);
});

let flag = ws.write("1", "utf8", () => {
  console.log("ok1");
});
// console.log(flag);
flag = ws.write("10", "utf8", () => {
  console.log("ok2");
});

// console.log(flag);
flag = ws.write("前端前端", "utf8", () => {
  console.log("ok222");
});

ws.on("drain", () => {
  console.log("drain");
});