最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • node中的fs和流

    正文概述 掘金(超级肥的兔子)   2020-12-09   665

    node中的fs和流

    • 读取文件默认编码是null, 二进制数据,文件不存在会报错
    • 写入文件默认以二进制写入,如果文件不存在会创建文件,如果文件存在会覆盖

    readFileSync

    同步读

    const read = fs.readFileSync(path.resolve(__dirname, 'name.txt'), 'utf8');
    console.log(read); //=>章三
    

    writeFileSync

    同步写

    fs.writeFileSync(path.resolve(__dirname, 'age.txt'), '18', 'utf8');
    

    readFile

    异步读

    fs.readFile(path.resolve(__dirname, 'name.txt'), (err, data) => {
        if (err) throw err;
        console.log(data.toString()); //=>章三
    })
    

    writeFile

    异步写

    fs.writeFile(path.resolve(__dirname, 'age.txt'), '20', err => {
        if (err) throw err;
    })
    

    createReadStream

    使用

    const fs = require('fs');
    const path = require('path');
    
    const rs = fs.createReadStream(path.resolve(__dirname, './number.txt'), {
        flags: 'r',
        encoding: null,
        // mode: 0o66,//权限,2爷1直4读书
        autoClose: true,
        start: 0,
        end: 6, //从索引0的位置开始读到索引3的位置,闭区间[0,6],读取了7个字节
        highWaterMark: 3, //每次读取的字节数,
    })
    
    rs.on('open', (fd) => {
        console.log(fd);
    })
    
    const arr = []
    //当监听data事件时,内部会不停的将数据发送过来
    rs.on('data', (thunk) => {
        console.log(thunk);
        arr.push(thunk);
    })
    
    rs.on('end', () => {
        console.log(Buffer.concat(arr).toString()); //=>0123456
    })
    
    rs.on('close', () => {
        console.log('close');
    })
    
    //rs.pause()可以暂停读取
    //rs.resume()可以恢复读
    

    简单实现

    主要使用的是fs的 open , read

    const EventEmitter = require('events');
    const fs = require('fs');
    
    class ReadStream extends EventEmitter {
        constructor(path, options) {
            super()
            //读取的文件路径
            this.path = path;
            //
            this.flags = options.flags || 'r';
            //读取索引的开始位置
            this.start = options.start || 0;
            //读取索引的结束位置
            this.end = options.end || undefined;
            //读取的编码格式
            this.encoding = options.encoding || null;
            //是否自动关闭
            this.autoClose = options.autoClose || true;
            //每次读取的字符数
            this.hightWaterMark = options.hightWaterMark || 64 * 1024;
            //偏移量
            this.offset = 0;
            //是不是流动模式
            this.flowing = false;
    
            //打开文件
            this.open()
    
            //监听用户注册data方法
            this.on('newListener', (type) => {
                if (type === 'data') { //用户注册了data
                    this.flowing = true; //设置为流动
                    //开始读文件
                    this._read()
                }
            })
        }
    
        //打开文件
        open() {
            fs.open(this.path, this.flags, (err, fd) => {
                if (err) throw err;
                this.fd = fd;
                this.emit('open', fd);
            })
        }
    
        //读文件
        _read() {
            //因为打开文件是异步操作,所以此时获取不到fd
            if (typeof this.fd !== 'number') return this.once('open', this._read);
            //计算读取的数量,如果end有值,就有通过end和hightWaterMark计算
            //因为start end是一个闭区间,所以要+1 
            const howMouchToRead = this.end ? Math.min(this.hightWaterMark, this.end - this.offset + 1) : this.hightWaterMark;
    
            const buffer = Buffer.alloc(howMouchToRead);
            //开始读取文件
            fs.read(this.fd, buffer, 0, howMouchToRead, this.offset, (err, bytesRead, buf) => {
                if (err) return this.destroy(err);
                //bytesRead真是读取到的字节个数
                if (bytesRead > 0) {
                    //发送数据给客户注册的data
                    this.emit('data', buf);
                    this.offset += bytesRead;
                    if (this.flowing) {
                        this._read();
                    }
                } else {
                    //读取完成
                    this.emit('end');
                    this.destroy();
                }
            })
        }
    
        //开始操作
        resume() {
            //确定已经暂停了
            if (!this.flowing) {
                this.flowing = true;
                this._read();
            }
        }
    
        //暂停操作
        pause() {
            this.flowing = false;
        }
    
        //结束
        destroy(err) {
            if (err) return this.emit('error', err);
            if (typeof this.fd === 'number' && this.autoClose) {
                fs.close(this.fd, () => {
                    this.emit('close');
                })
            }
        }
    }
    
    module.exports = ReadStream;
    

    createWriteStream

    • 写的过程会排序,开始写的时候,会讲第一个真正的写入,其他的放入到链表中,等待第一个写入完成,在从链表中拿出第一个再写入
    • highWaterMark: 我期望使用多少个字节完成写入操作,如果超出后write的返回值会变成false, 返回false 可以用于判断,告知用户不要在写入了,再写入只能放到内存中,占用内存

    简单使用

    const path = require('path');
    const fs = require('fs');
    
    const ws = fs.createWriteStream(path.resolve(__dirname, './copy.txt'), {
        flags: 'w',
        encoding: 'utf8',
        mode: 0o666,
        autoClose: true,
        emitClose: true,
        start: 0,
        highWaterMark: 3
    });
    
    //需要注意的是write的返回值,当写入的字符个数>=highWaterMark的值时,返回的是false
    //在下面写入的过程是会将012中的0第一次真正的写入,而将12会放入链表中等待0写入完成
    //在从链表中取出第一个1来继续写入,依次下去,直到写完。这里第二个r返回的是false,因为012是
    //3个字符>=highWaterMark的3
    let r = ws.write('01', (err) => {
        if (err) throw err;
    })
    console.log(r); //=>true
    
    r = ws.write('2', (err) => {
        if (err) throw err;
    })
    console.log(r); //=>false
    

    希望写入10个数(0-9),只用一个字节的内存

    const fs = require('fs');
    const path = require('path');
    
    const ws = fs.createWriteStream(path.resolve(__dirname, './copy.txt'), {
        flags: 'w',
        encoding: 'utf8',
        mode: 0o666,
        autoClose: true,
        emitClose: true,
        start: 0,
        highWaterMark: 1
    })
    
    let index = 0;
    
    function write() {
        let writting = true;
        while (index < 10) {
            //这里写入时,不能为number类型,不然回报错
            writting = ws.write(`${index++}`);
            if (!writting) {
                break;
            }
        }
        if (index === 10) {
            ws.end(); //当执行end后会触发close事件,并且end就不会再触发drain事件了,可以看下log,注释掉end会打印10个drain,不注释掉会打印9个drain和一个close
            // ws.end('yy');//end可以在写一条数据,相当于end=write+close
        }
    }
    
    //当写入的个数到达期望值或超过(highWaterMark)后被消费,会触发drain事件
    ws.on('drain', () => {
        console.log('drain');
        write();
    })
    
    ws.on('close', () => {
        console.log('close');
    })
    
    //执行
    write();
    

    实现writeStream

    主要是用的是 fsopen , write

    const fs = require('fs');
    const EventEmitter = require('events');
    
    class WriteStream extends EventEmitter {
        constructor(path, options = {}) {
            super();
            this.path = path;
            this.flags = options.flags || 'w';
            this.encoding = options.encoding || 'utf8';
            this.mode = options.mode || 0o666;
            this.autoClose = options.autoClose || true;
            this.emitClose = options.emitClose || true;
            this.start = options.start || 0;
            this.highWaterMark = options.highWaterMark || 16 * 1024;
    
            this.offset = 0;
            //默认不是正在写入,第一次调用write的时候需要执行fs.write方法
            this.writting = false;
            //表示需要写入的长度
            this.len = 0;
            //是否触发drain事件
            this.needDrain = false;
            //缓存
            this.cache = [];
    
            //打开文件
            this.open();
    
        }
    
        open() {
            fs.open(this.path, this.flags, this.mode, (err, fd) => {
                if (err) this.destory(err);
                this.fd = fd;
                this.emit('open', fd);
            })
        }
    
        write(chunk, encoding = this.encoding, cb = () => {}) {
            //用户写的东西可能是汉字
            chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
            //需要写入的长度
            this.len += chunk.length;
            //返回结果,如果写入的<highWaterMark
            const result = this.len < this.highWaterMark;
            //跟返回的result刚好相反
            this.needDrain = !result
    
            const clearBuffer = () => {
                this.clearBuffer();
                cb();
            }
            if (this.writting) {
                //剩下的放到缓存中
                this.cache.push({
                    chunk,
                    encoding,
                    clearBuffer
                })
            } else {
                //第一次的时候是真的写到文件中
                this.writting = true;
                //真正的去写
                this._write(chunk, encoding, clearBuffer);
            }
    
            return result;
        }
    
        _write(chunk, encoding, cb) {
            //open是异步过程,这里fd还拿不到
            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, written) => {
                if (err) return this.destory(err);
                //修改offset
                this.offset += written;
                //修改len
                this.len -= written;
                //写入完成需要清空缓存
                cb();
            })
        }
    
        clearBuffer() {
            //从缓存中拿出一个数据
            const data = this.cache.shift();
            if (data) { //缓存中有内容
                //将数据继续写入文件中
                this._write(data.chunk, data.encoding, data.clearBuffer);
            } else {
                //缓存中已经没有数据了
                this.writting = false;
                if (this.needDrain) {
                    this.needDrain = false;
                    this.emit('drain');
                }
            }
        }
    
        destory(err) {
            if (err) this.emit('error', err);
        }
    }
    
    module.exports = WriteStream;
    

    起源地下载网 » node中的fs和流

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元