ReadStream & WriteStream 实现

Stream

Stream 是一个抽象接口,Node 中有很多对象实现了这个接口。例如,对http 服务器发起请求的request 对象就是一个 Stream,还有stdout(标准输出)。 Node.js,Stream 有四种流类型:(流有两种模式:暂停模式、流动模式)

-详细的api介绍请查看nodejs.cn/api/stream.…

  • Readable -- 可读操作。
Writable -- 可写操作。 Duplex -- 可读可写操作. Transform -- 操作被写入数据,然后读出结果。

可写流(createWriteStream)

  • on('drain')
  • ws.write(chuck, encoding, callbck) 返回Boolen值 第一次真的写入,之后写入放放缓存区
let fs = require('fs');
let ws = fs.createWriteStream('./test2.txt', {
    flags: 'w', //读取的方式
    encoding: 'utf8', //编码
    autoClose: true,
    start: 0,
    highWaterMark: 3 //最高水位线
});
//第一次会入到文件内,后面写到缓存中
//write方法. chuck --> string or buffer;
let flag = ws.write('1');
console.log(flag);
flag = ws.write('1');
console.log(flag);
flag = ws.write('1');
console.log(flag);
//highWaterMark 是个标识,配合读取用
//highWaterMark 使用内存大小  默认64k, 读取后超出最高水位线应该暂停

//写完调用drain方法
ws.on('drain', () => {
    console.log('抽干')
})
//drain方法,必须当前的写入内容已经大于highWater才会触发drain
//当内容全部写入后,执行drain方法

ws.end('关闭了');//会将缓存内容清空后.再关闭文件
ws.write('Ok') //不能再结束后,继续写入
复制代码

实现可写流

let fs = require('fs');
let EventEmitter = require('events');

class WriteStream extends EventEmitter {
    constructor (path, options = {}) {
        super();
        this.path = path;
        this.autoClose = options.autoClose || true;
        this.encoding = options.encoding || null;
        this.highWaterMark = options.highWaterMark || 16 * 1024;
        this.mode = options.mode || 0o666;
        this.start = options.start || 0;
        this.flags = options.flags || 'w';

        //是否需要触发drain事件
        this.needDrain = false;
        //是否正在写入
        this.writing = false;
        //正在写入放在缓存中
        this.buffer = [];
        //缓存个数 
        this.len = 0;
        //写入时候的位置关系 
        this.pos = this.start;
        
        this.open();
    }
    destroy(){
        if(typeof this.fd === 'number'){
          fs.close(this.fd, () =>{
            this.emit('close');
          });
          return 
        }
        this.emit('close');
    }
    open() {
        fs.open(this.path, this.flags, this.mode, (err, fd)=> {
            if(err){
                this.emit('error');
                this.destroy();
                return 
            }
            this.fd = fd;
            this.emit('open');
        });
    }
    write(chunk, encoding = this.encoding, callback) {
        chunk = Buffer.isBuffer(chunk) ? chunk:Buffer.from(chunk);
        this.len += chunk.length;// 每次调用write就统计一下长度

        this.needDrain = this.highWaterMark <= this.len; 
        if (this.writing) {
           this.buffer.push({chunk, encoding, callback})
        }else {
            //当文件写入后,清空缓存区的内容
            this.writing = true; //走缓存
            this._write(chunk, encoding, () => this.clearBuffer());
        };
        return !this.needDrain;
    }
    _write(chunk, encoding, callback) {
        if (typeof this.fd !== 'number') {
            return this.once('open', () => this._write(
                chunk, encoding, callback
            ))
        }
        /*
           @params: fd --> Number --> 文件描述符
           @params: chuck -->数据
           @parmas: offset --> 写入位置
           @params: length --> 写入长度
           @params: position --> 偏移量
        */
        fs.write(this.fd, chunk, 0, chunk.length, this.pos, (err, bytesWritten) => {
            this.pos += bytesWritten;
            this.len -= bytesWritten; //写扩的长度会减少
            callback();
        })
    }
    clearBuffer() {
        let buf = this.buffer.shift();
        if(buf){
          this._write(buf.chunk, buf.encoding, () => this.clearBuffer());
        }else{
          this.writing = false;
          this.needDrain = false; // 触发一次drain  再置回false 方便下次继续判断
          this.emit('drain');
        }
    }
}

module.exports = WriteStream;
复制代码

验证可写流实现

let fs = require('fs');
let WriteStream = require('./WriteStream');
let ws = new WriteStream('./3.txt', {
    flags: 'w',
    highWaterMark: 3,
    autoClose: true,
    encoding: 'utf8',
    start: 0
});
let i = 0;
function write() {
    let flag = true;
    while ( i < 9 && flag) {
        flag = ws.write(i++ + '');
    }
}

ws.on('drain', () =>{
    console.log('写入成功')
    write();
})
write();
输出结果如下
写入成功
写入成功
写入成功
复制代码

可读流(createReadStream)

  • on('open') 打开
  • on('data') 流动模式
  • on('end') 结束
  • on('close') 关闭
  • rs.pause 暂停
  • rs.resume 恢复
//读文件(文件需存在)
let fs = require('fs');
let rs = fs.createReadStream('./tes1t.txt', {
    flags: 'r', //读取的方式
    encoding: null, //编码 buffer
    autoClose: true,
    start: 0,
    end: 9, // 包后
    highWaterMark: 2 //最高水位线
})
//默认情况下,不会读取文件内
let arr = [];
rs.on('data', (data) => {
    rs.pause(); //暂停 暂停触发data事件
    arr.push(data);
    console.log("停")
    setTimeout(() => {
        rs.resume(); //恢复
    })
})
rs.on('error', (err) => {
    console.log("err", err)
})
rs.on('end', function(){
    console.log(Buffer.concat(arr).toString())
})
复制代码

实现可读流

let EventEmitter = require('events');
let fs = require('fs');

class ReadStream extends EventEmitter {
    constructor(path, options = {}) {
        super();
        this.path = path;
        this.autoClose = options.autoClose || true;
        this.flags = options.flags || 'r';
        this.encoding = options.encoding || null;
        this.start = options.start || 0;
        this.end = options.end || null;
        this.highWaterMark = options.highWaterMark || 64 * 1024;
        //读取文件的位置(可变的)
        this.pos = this.start;

        //控制当前是否是流动模式
        this.flowing = null;
        //构建读取内容buffer
        this.buffer = Buffer.alloc(this.highWaterMark);
        //当创建可读流,要将文件打开 --> 异步执行
        this.open();

        this.on('newListener', (type) => {
            //用户监听data事件
            if (type === 'data') {
                //改成流动模式
                this.flowing = true;
                this.read();
            }
        })
    }
    read() {
        //文件还没有打开,等待文件后再去读取
        if (typeof this.fd !== 'number') {
            //等待文件打开,再次调用援藏
            return this.once('open', () => this.read());
        }
        //开始读取
        /*
           @params: fd --> Number --> 文件描述符
           @params: buffer --> 读到buffer里
           @parmas: offset --> 读取到buffer的位置
           @params: length --> 读取几个到buffer里
           @params: position --> 读取文件的位置
        */
        let howMuchToRead = this.end ? Math.min(this.end - this.pos + 1, this.highWaterMark) : this.highWaterMark
        fs.read(this.fd, this.buffer, 0, howMuchToRead, this.pos, (err, bytesRead) => {
            if (err) {
                this.emit('error');
                this.destroy();
                return;
            }
            if (bytesRead > 0) {
                this.pos += bytesRead;
                //保留有效的
                let r = this.buffer.slice(0, bytesRead);
                r = this.encoding ? r.toString(this.encoding) : r;
                //第一次读取
                this.emit('data', r);
                if (this.flowing) {
                    this.read();
                }
            }else {
                this.emit('end');
                this.destroy();
            } 
        });
    }
    destroy() {
        //判断文件是否打开(交文件关闭)
        if (typeof this.fd === 'number') {
            fs.close(this.fd, () => {
                this.emit('close');
            });
            return;
        }
        this.close('close');
    }
    open() {
        fs.open(this.path, this.flags, (err, fd) => {
            if (err) {
                this.emit('error', err);
                if (this.autoClose) {
                    // 销毁,关闭文件(触发close事件)
                    this.destroy();
                }
                return;
            }
            this.fd = fd;
            //触发文件开户事件
            this.emit('open');
        })
    }
    pause() {
        this.flowing = false;
    }
    resume() {
        this.flowing = true;
        this.read();
    }
   
}

module.exports = ReadStream;
复制代码

验证可读流实现

let fs = require('fs');
let ReadStream = require('./ReadStream.js');
let rs = new ReadStream('./test.txt', {
    flags: 'r', //读取的方式
    encoding: null, //编码buffer,
    autoClose: true,
    start: 0,
    end: 4, // 包后
    highWaterMark: 2, //最高水位线,
    encoding: 'utf8'
})
let arr = [];
rs.on('open', () => {
    console.log('open');
})
rs.on('data', (data) => {
   console.log(data);
   rs.pause();
   setTimeout(() => {
    rs.resume()
}, 1000)
});
rs.on('error', (err) => {
    console.log("err", err)
})
rs.on('end', function(){
   console.log("完毕");
})
rs.on('close', function(){
    console.log("close");
});
输出结果如下:
open
12
34
5
完毕
close
复制代码

转载于:https://juejin.im/post/5b43795951882519eb657d0b

你可能感兴趣的:(ReadStream & WriteStream 实现)