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
复制代码