文件可写流的实现

104 阅读1分钟
const fs = require('fs')
const path = require('path')

const EventEmitter = require('events')
let LinkList = require('./linkList')

class Queue {
    constructor(){
        this.LinkList = new LinkList()
    }
    offer(element){
        this.LinkList.add(element)
    }
    poll(){
        return this.LinkList.remove(0)
    }
}

class WriteStream extends EventEmitter {
    constructor(path,options){
        super();
        this.path = path;
        this.flags = options.flags || 'w';
        this.autoClose = options.autoClose || true;
        this.encoding = options.encoding || 'utf8';
        this.start = options.start || 0;
        this.mode = options.mode || 0o666;
        this.highWaterMark = options.highWaterMark || 16 * 1024
    
        //维护当前存入的数据个数
        this.len = 0;//每次调用write方法 会根据写入的内容个数累加给len属性(缓存的长度)
        this.writing = false; //当前写入的时候是否正在写入
        this.needDrain = false;//是否需要触发drain事件(触发drain事件)
        this.offset = this.start;//写入时的偏移量
        this.cache = new Queue();//用来缓存 多次的写入操作
        this.open() //默认先打开文件
    }
    open() {
        fs.open(this.path, this.flags,this.mode,(err,fd) => {
            if(err) return this.emit('error',err);
            this.fd = fd
            this.emit('open',fd)
        })
    }
    write(chunk,encoding = 'utf8',cb=()=>{}){
        chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
        this.len += chunk.length
        let flag = this.len < this.highWaterMark; //这就是write返回的值
        this.needDrain = !flag //drain事件的触发
        if(this.writing){
            //正在写入
            this.cache.offer({
                chunk,encoding,cb
            })
        }else{
            this.writing = true
            this._write(chunk,encoding,()=>{
                cb()
                this.clearBuffer()
            })
        }
        return flag
    }
    _write(chunk,encoding,cb){
        if(typeof this.fd !== 'number'){
            return this.once('open',() => this._write(chunk,encoding,cb))
        }
        fs.write(this.fd,chunk,0,chunk.length,this.offset,(err,writen) => {
            this.len -= writen
            this.offset += writen
            cb()
        })
    }
    clearBuffer(){
        let data = this.cache.poll()
        if(data){
            let {chunk,encoding,cb} = data
            this._write(chunk,encoding,() =>{
                cb()
                this.clearBuffer()
            })
        }else{
            this.writing = false
            if(this.needDrain){
                this.needDrain = false
                this.emit('drian')
            }
        }
    }
}