nodeJs流的使用及原理

本文详细介绍了Node.js中文件流的使用方法,包括可读流和可写流的创建及事件处理过程。通过示例代码展示了如何设置流的选项参数,并解释了流的工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

node的流,就是对文件的处理,包括可读流,可写流,双工流,压缩流

可读流主要是对文件进行读取,或者是对输入进行读取

可写流主要是用来对文件进行写入,或者是进行输出


可读流写法

// readFile writeFile// 会将文件读取到内存中 占用内存
// fs.read fs.write 文件模块中 单独提供了文件的读流和写流
// 有起点和终点// 每次读几个
// 读流中读取出内容 导入到写流中// 流有多种方式
// 文件的可读流let fs = require('fs');
let ReadStream = require('./ReadStream3')
let rs = new ReadStream('1.txt', {
    encoding: null, // 编码
    flags: 'r+', // 标识要读取文件
    highWaterMark: 3, // 最高水位线,每次读多少个
    autoClose: true, // 读取后关闭
    start: 0, // 开始读取的位置
    end: 3  // 结束的位置
});

// 事件机制,需要自己去监听一些数据
// 默认不流动 非流动模式,需要监听data事件,内部会按照highwaterMark 读取数据把数据在内部发射出来
let arr = [];
rs.on('open', function () {
    console.log(' 文件开启了')
});

rs.on('data', function (data) {
    console.log(data);  arr.push(data);
});

rs.on('end', function () { // 只有目标文件读取完毕后才触发
    console.log('结束了');
    console.log(Buffer.concat(arr).toString());
});// rs.pause()

rs.on('error', function (err) {
    console.log('出错了')
})
rs.on('close', function () {
    console.log('close')
});

复制代码



fs.createReadStream方法

参数(path, options = { enconding:编码, flags: 处理的类型, highWaterMark: 最高水位线,每次读取的最大字节, autoClose文件读取完成后是否自动关闭, start:开始读取的位置, end:结束读取的位置 })

返回一个rs实例,使用发布订阅模式

rs.on('open', callback)    rs.on('data', callback)   rs.on('end', callback) 

open是文件刚打开触发的事件

data是每次读取的内容(会根据每次读取的highWaterMark分多次读取)

end是文件读取结束触发的事件


下面我们来解析代码

let EventEmitter = require('events')
let fs = require('fs')
// 需要继承EventEmitter
// 使用发布订阅模式处理回调问题
class ReadStream extends EventEmitter {
    constructor(path, options = {}) {
        super()
        // 读取文件的路径
        this.path = path
        // 编码类型
        this.encoding = options.encoding || null
        // 处理文件的模式默认读取
        this.flags = options.flags || 'r'
        // 最大水位线默认64kb
        this.highWaterMark = options.highWaterMark || 64 * 1024
        // 是否自动关闭,结束读取或者error后是否会自动关闭fd
        this.autoClose = options.autoClose || true
        // 开始读取的位置
        this.start = options.start || 0
        // 结束读取的位置
        this.end = options.end || null
        // 标记文件读取到的位置
        this.pos = this.start
        // 文件流动模式,用来处理读取的开始和暂停
        this.flowing = false
        // 初始化打开文件
        this.open()
        this.on('newListener', type => {
            // 如果是data事件才开始读取
            if (type === 'data') {
                this.flowing = true
                this.read()
            }
        })
    }
    destory() {
        if (!this.fd) {
            this.emit('close')
        } else {
            // 如果有fd说明文件打开过,需要关闭fd
            fs.close(this.fd, () => {
                this.emit('close')
            })
        }
    }
    open() {
        fs.open(this.path, this.flags, (err, fd) => {
            if (err) {
                this.emit('error', err)
                this.autoClose && this.destory()
            } else {
                this.fd = fd
                // 打开触发open事件
                this.emit('open', fd)
            }
        })
    }
    pause() {
        // 关闭流动模式,暂停
        this.flowing = false
    }
    resume() {
        // 恢复流动,开启
        this.flowing = true
        this.read()
    }
    // 文件读取方法
    read() {
        if (!this.fd) return this.once('open', () => this.read())
        if (!this.flowing) return false
        // 创建buffer判断
        // howMuchRead是取最后到当前位置和highWaterMark最小值,防止溢出
        let howMuchRead = this.end ? Math.min(this.end - this.pos + 1, this.highWaterMark) : this.highWaterMark
        let buffer = Buffer.alloc(howMuchRead)
        fs.read(this.fd, buffer, 0, howMuchRead, this.pos, (err, bytesRead) => {
            if (err) {
                this.emit('error', err)
                this.autoClose && this.destory()
            } else {
                if (bytesRead > 0) {
                    this.pos += bytesRead
                    this.emit('data', buffer.slice(0, bytesRead))
                    this.read()
                } else {
                    this.emit('end')
                    this.autoClose && this.destory()
                }
            }
        })
    }
}
module.exports = ReadStream
复制代码


下面我们来看看可写流用法

let fs = require('fs')
let ws = fs.createWriteStream('2.txt', {
    flags: 'w',          //行为
    encoding: 'utf8',    //编码
    mode: 0o666,         //权限
    autoClose: true,     //是否自动关闭
    start: 0,            //开始位置
    highWaterMark: 3     //最大水位线
})

let flag = ws.write('hello', 'utf8', () => console.log('ok'))
console.log(flag)
//flag用来标记流是否低于最大水位线,此时应该是false
flag = ws.write('2')

ws.on('drain', () => {
    console.log('我drain了')
})复制代码

ws.write()方法会将内容写入目标文件,写入的过程是异步的,会有一个缓存区,如果多次write会将第一次需要写入的内容进行写入操作,后面的会存入缓存区,等待第一次写入结束再从缓存区拿出来继续写入

如果写入的值超过了highWaterMark,会触发darin事件

那么我们来看看如何实现写入流的吧

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

class WriteStream extends EventEmitter {
    constructor(path, options = { }) {
        super()
        this.path = path
        this.flags = options.flags || 'w'
        this.encoding = options.encoding || null
        this.mode = options.mode || 0o666
        this.autoClose = options.autoClose || true
        this.start = options.start || 0
        this.highWaterMark = options.highWaterMark || 16 * 1024
        // 偏移量
        this.pos = this.start
        // 是否正在写入
        this.writing = false
        // 缓存区
        this.cache = []
        // 记录长度
        this.len = 0
        // 写入完毕是否触发阀值
        this.needDrain = false
        this.open()
    }
    // 销毁方法
    destory() {
        if (!this.fd) {
            this.emit('close')
        } else {
            fs.fsync(this.fd, () => {
                fs.close(this.fd, () => {
                    this.emit('close')
                })
            })
        }
    }
    open() {
        fs.open(this.path, this.flags, (err, fd) => {
            if (err) {
                this.emit('error', err)
                this.autoClose && this.destory()
            } else {
                this.fd = fd
                this.emit('open', fd)
            }
        })
    }
    write(chunk, encoding, callback) {
        chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
        this.len += chunk.length
        if (this.len >= this.highWaterMark) {
            this.needDrain = true
        }
        if (this.writing) {
            this.cache.push({chunk, encoding, callback})
        } else {
            this._write(chunk, encoding, () => {callback(); this.clearBuffer()})
        }
        return this.highWaterMark > this.len
    }
    clearBuffer() {
        let obj = this.cache.shift()
        if (obj) {
            this._write(obj.chunk, obj.encoding, () => {obj.callback(); this.clearBuffer()})
        } else {
            this.len = 0
            this.writing = false
            if (this.needDrain) {
                this.needDrain = false
                this.emit('drain')
            }
        }
    }
    _write(chunk, encoding, callback) {
        if (!this.fd) return this.emit('open', () => this._write(chunk, encoding, callback))
        fs.write(this.fd, chunk, 0, chunk.length, this.pos, (err, bytesWritting) => {
            if (err) {
                this.writing = false
                this.emit('error', err)
                this.autoClose && this.destory()
            } else {
                this.pos += bytesWritting
                this.emit('data', chunk)
                callback()
            }
        })
    }
}
module.exports = WriteStream


复制代码


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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值