可读流
let EventEmitter = require('events');
let fs = require('fs');
class ReadStream extends EventEmitter {
constructor (path, options) {
super();
this.path = path;
this.flags = options.flags || 'r';
this.autoClose = options.autoClose || true;
this.highWaterMark = options.highWaterMark || 64 * 1024;
this.start = options.start || 0;
this.end = options.end;
this.encoding = options.encoding || null;
this.flowing= null;
this.buffer = Buffer.alloc(this.highWaterMark);
this.pos = this.start;
this.on('newListener', (eventName, callback) => {
if (eventName === 'data') {
this.flowing = true;
this.read();
}
});
this.open();
}
destroy () {
if (typeof this.fd === 'number') {
fs.close(this.fd, () => {
this.emit('close');
});
return;
}
this.emit('close');
}
resume () {
this.flowing = true;
this.read();
}
pause () {
this.flowing = false;
}
open () {
fs.open(this.path, this.flags, (err, fd) => {
if (err) {
this.emit('error', err);
if (this.autoClose) {
this.destroy();
}
return
}
this.fd = fd;
this.emit('open')
})
}
read () {
if (typeof this.fd !== 'number') {
return this.once('open', () => this.read());
}
let howMuchToRead = this.end ? Math.min(this.highWaterMark,this.end-this.pos+1) : this.highWaterMark;
fs.read(this.fd, this.buffer, 0, howMuchToRead, this.pos, (err, bytesRead) => {
if (bytesRead > 0) {
this.pos += bytesRead;
let data = this.encoding ? this.buffer.slice(0, bytesRead).toString(this.encoding) : this.buffer.slice(0, bytesRead);
this.emit('data', data);
if (this.pos > this.end) {
this.emit('end');
this.destroy();
return
}
if (this.flowing) {
this.read();
}
} else {
this.emit('end');
this.destroy();
}
});
}
pipe (ws) {
this.on('data', (chunk) => {
let flag = ws.write(chunk);
if (!flag) {
this.pause();
}
});
ws.on('drain', () => {
this.resume();
});
}
}
module.exports = ReadStream;
可写流
let EventEmitter = require('events');
let fs = require('fs');
class WriteStream extends EventEmitter {
constructor (path, options) {
super();
this.path = path;
this.highWaterMark = options.highWaterMark || 16 * 1024
this.autoClose = options.autoClose || true;
this.mode = options.mode || 0o666;
this.start = this.start || 0;
this.flags = options.flags || 'w';
this.encoding = options.encoding || 'utf-8';
this.buffers = [];
this.length = 0;
this.writing = false;
this.needDrain = false;
this.pos = this.start;
this.open();
}
open () {
fs.open(this.path, this.flags, this.mode, (err, fd) => {
if (err) {
this.emit('error', err);
if (this.autoClose) {
this.destroy();
}
return;
}
this.fd = fd;
this.emit('open');
});
}
destroy () {
if (typeof this.fd !== 'number') {
return this.emit('close');
}
fs.close(this.fd, () => {
this.emit('close');
});
}
write (chunk, encoding = this.encoding, callback = () => {}) {
chunk = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk, encoding);
this.length += chunk.length;
let ret = this.length < this.highWaterMark;
this.needDrain = !ret;
if (this.writing) {
this.buffers.push({
chunk,
encoding,
callback
});
} else {
this.writing = true;
this._write(chunk, encoding, () => {
callback();
this.clearBuffer();
});
}
return ret;
}
clearBuffer () {
let buffer = this.buffers.shift();
if (buffer) {
this._write(buffer.chunk, buffer.encoding, () => {
buffer.callback();
this.clearBuffer();
})
} else {
this.writing = false;
if (this.needDrain) {
this.needDrain = false;
this.emit('drain');
}
}
}
_write (chunk, encoding, callback) {
if (typeof this.fd !== 'number') {
return this.once('open', () => this._write(chunk, encoding, callback));
}
fs.write(this.fd, chunk, 0, chunk.length, this.pos, (err, bytesWritten) => {
this.length -= bytesWritten;
this.pos += bytesWritten;
callback();
});
}
}
module.exports = WriteStream;