模拟文件汇总
文件可读流
const fs = require('fs')
const EventEmitter = require('events')
class MyFileReadStream extends EventEmitter {
constructor(path, options = {}) {
super()
this.fd = null
this.path = path
this.flags = options.flags || 'r'
this.mode = options.mode || 438
this.autoClose = options.autoClose || true
this.start = options.start || 0
this.end = options.end
this.highWaterMark = options.highWaterMark || 64 * 1024
this.readOffset = 0
this.flowing = true
this.open()
this.on('newListener', type => {
if (type === 'data') {
this.read()
}
})
}
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)
})
}
read() {
if (typeof this.fd !== 'number') {
return this.once('open', this.read)
}
const buf = Buffer.alloc(this.highWaterMark)
const howMuchToRead = this.end ? Math.min(this.end - this.readOffset + 1, this.highWaterMark) : this.highWaterMark
fs.read(this.fd, buf, 0, howMuchToRead, this.readOffset, (err, readBytes) => {
if (readBytes) {
this.readOffset += readBytes
console.log('read 结果:',buf.slice(0, readBytes).toString());
this.emit('data', buf.slice(0, readBytes))
if (this.flowing) {
this.read()
}
} else {
this.emit('end')
if (this.autoClose) {
this.close()
}
}
})
}
close() {
fs.close(this.fd, () => {
this.emit('close')
})
}
pause() {
if (this.flowing) {
this.flowing = false
}
}
resume() {
if (!this.flowing) {
this.flowing = true
this.read()
}
}
pipe(ws) {
this.on('data', chunk => {
const flag = ws.write(chunk)
if (!flag) {
this.pause()
ws.on('drain', () => {
this.resume()
})
}
})
}
}
module.exports = MyFileReadStream
单向链表队列
class Node {
constructor(element, next) {
this.element = element
this.next = next
}
}
class LinkedList {
constructor() {
this.head = null
this.size = 0
}
_getNode(index) {
if (index < 0 || index >= this.size) {
throw new Error('越界了')
}
let currentNode = this.head
for (let i = 0; i < index; i++) {
currentNode = currentNode.next
}
return currentNode
}
add(index, element) {
if (arguments.length === 1) {
element = index
index = this.size
}
if (index < 0 || index > this.size) {
throw new Error('越界了')
}
if (index === 0) {
const head = this.head
this.head = new Node(element, head)
} else {
const prevNode = this._getNode(index - 1)
prevNode.next = new Node(element, prevNode.next)
}
this.size++
}
remove(index) {
let rmNode = null
if (index === 0) {
rmNode = this.head
if (!rmNode) {
return undefined
}
this.head = rmNode.next
} else {
const prev = this._getNode(index - 1)
rmNode = prev.next
prev.next = rmNode.next
}
this.size--
return rmNode
}
set(index, element) {
const node = this._getNode(index)
node.element = element
}
get(index) {
return this._getNode(index)
}
clear() {
this.head = null
}
}
class Queue {
constructor() {
this.linkedList = new LinkedList()
}
enQueue(data) {
this.linkedList.add(data)
}
deQueue() {
return this.linkedList.remove(0)
}
}
module.exports = Queue
文件可写流
const fs = require('fs')
const EventEmitter = require('events')
const Queue = require('./linked-queue')
class MyFileWriteStream extends EventEmitter {
constructor(path, options) {
super()
this.path = path
this.flags = options.flags || 'w'
this.mode = options.mode || 438
this.autoClose = options.autoClose || true
this.start = options.start || 0
this.encoding = options.encoding || 'utf8'
this.highWaterMark = options.highWaterMark || 16 * 1024
this.open()
this.writeOffset = this.start
this.writing = false
this.length = 0
this.needDrain = false
this.cache = new Queue()
}
open() {
fs.open(this.path, this.flags, (err, fd) => {
if (err) {
return this.emit('error', err)
}
this.fd = fd
this.emit('open', fd)
})
}
write(chunk, encoding, cb) {
chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk)
this.length += chunk.length
const flag = this.length < this.highWaterMark
this.needDrain = !flag
if (this.writing) {
this.cache.enQueue({ chunk, encoding, cb })
} else {
this.writing = true
this._write(chunk, encoding, cb)
}
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, this.start, chunk.length, this.writeOffset, (err, written) => {
this.writeOffset += written
this.length -= written
cb && cb()
this._clearBuffer()
})
}
_clearBuffer() {
const data = this.cache.deQueue()
if (data) {
this._write(data.element.chunk, data.element.encoding, data.element.cb)
} else {
if (this.needDrain) {
this.writing = false
this.needDrain = false
this.emit('drain')
}
}
}
}
module.exports = MyFileWriteStream