stream简述
stream分为四种,如下:
- stream.Readable — 输入流
- stream.Writable — 输出流
- stream.Duplex — 双工流
- stream.Transform — 转换流
流拥有两种模式
- 二进制模式,以Buffer、String、Uint8Array
- 对象模式,流的内部是对象形式
输入流(stream.Readable)
输入流有两种模式,一种是流动模式,另一种是非流动模式
非流动模式就是监听data方法,直接读取read不暂停,不存到缓存区
流动模式就是监听readable方法,就是讲读取内容放到缓存区内,等待writable调用,在判断是否有空位,在取消暂停
Readable源码分析
首先看下面Readable的源码,对照Readable源码再看stream做了哪些操作
_stream_readable.js文件
// 首先监听data或者readable的时候,进入Readable.prototype.on —— 778行
Readable.prototype.on = function(ev, fn) {//传进来一个ev代表监听参数,fn回调函数
const res = Stream.prototype.on.call(this, ev, fn);//继承Stream的on方法,传入ev,fn
if (ev === 'data') {//监听data
// Start flowing on next tick if stream isn't explicitly paused
if (this._readableState.flowing !== false)//flowing 是在Readable函数中定义看下面Readable函数,不等于false代表流动模式
this.resume();// 开始读取
} else if (ev === 'readable') {//监听readable
const state = this._readableState;//设置state常量为_readableState,具体看下面Readable函数
if (!state.endEmitted && !state.readableListening) {//如果ended没有触发或者不为流动模式,则readableListening,needReadable为true让其成为流动模式,并且需要Readable,不触发Readable
state.readableListening = state.needReadable = true;
state.emittedReadable = false;
if (!state.reading) {//如果没有正在读取,则下一个事件环调用nReadingNextTick,见下代码
process.nextTick(nReadingNextTick, this);
} else if (state.length) {//如果缓存区长度存在,则执行emitReadable,代码见下
emitReadable(this);
}
}
}
return res;//返回res
};
// Readable函数 —— 141行
function Readable(options) {//将createReadStream中的options传入
if (!(this instanceof Readable))//判断有没有new Readable()
return new Readable(options);
this._readableState = new ReadableState(options, this);//设置ReadableState实例,命名为this._readableState,具体ReadableState构造函数看下方ReadableState函数函数
// legacy
this.readable = true;//、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、、
if (options) {//判断参数是否存在
if (typeof options.read === 'function')//判断是否调用的new Readable
this._read = options.read;//让函数内部_read方法指向read方法
if (typeof options.destroy === 'function')
this._destroy = options.destroy;//让函数内部_destroy方法指向destroy方法
}
Stream.call(this);//options不存在,继承Stream
}
// ReadableState函数 —— 59行
function ReadableState(options, stream) {//接收options和Readable上下文
options = options || {};//options为空则命options为空对象
// Duplex streams are both readable and writable, but share
// the same options object.
// However, some cases require setting options to different
// values for the readable and the writable sides of the duplex stream.
// These options can be provided separately as readableXXX and writableXXX.
var isDuplex = stream instanceof Stream.Duplex;//用来判断是否是双工流
// object stream flag. Used to make read(n) ignore n and to
// make all the buffer merging and length checks go away
this.objectMode = !!options.objectMode;//对象流的标识,如果是对象流忽视read中的n参数
if (isDuplex)//同this.objectMode用法一样
this.objectMode = this.objectMode || !!options.readableObjectMode;
// the point at which it stops calling _read() to fill the buffer
// Note: 0 is a valid value, means "don't call _read preemptively ever"
var hwm = options.highWaterMark;//获取传入highWaterMark值
var readableHwm = options.readableHighWaterMark;//设置可读流highWaterMark
var defaultHwm = this.objectMode ? 16 : 16 * 1024;//默认的highWaterMark
if (hwm || hwm === 0)
this.highWaterMark = hwm;//如果参数highWaterMark存在或者为0数值则设置此构造函数属性highWaterMark
else if (isDuplex && (readableHwm || readableHwm === 0))
this.highWaterMark = readableHwm;//如果是双工流并且可读流highWaterMark存在则设置此构造函数属性highWaterMark
else
this.highWaterMark = defaultHwm;//否则this.highWaterMark为默认值
// cast to ints.
this.highWaterMark = Math.floor(this.highWaterMark);//向下取整
// A linked list is used to store data chunks instead of an array because the
// linked list can remove elements from the beginning faster than
// array.shift()
this.buffer = new BufferList();//设置缓存区大小,采用BufferList,链式结构
this.length = 0;//设置缓存区长度
this.pipes = null;//下一个管道是否存在
this.pipesCount = 0;//设置管道数量
this.flowing = null;//设置是否为流动或非流动模式
this.ended = false;//Readable状态标识,true表示数据读取完毕
this.endEmitted = false;//Readable状态标识,为true表示ended已经触发
this.reading = false;//表示正在调用_read
// a flag to be able to tell if the event 'readable'/'data' is emitted
// immediately, or on a later tick. We set this to true at first, because
// any actions that shouldn't happen until "later" should generally also
// not happen before the first read call.
this.sync = true;//让emitReadable是在这个事件环还是下个事件环触发
// whenever we return null, then we set a flag to say
// that we're awaiting a 'readable' event emission.
this.needReadable = false;//是否需要Readable事件触发
this.emittedReadable = false;//触发Readable事件
this.readableListening = false;//是否准备切换流动模式
this.resumeScheduled = false;
// has it been destroyed
this.destroyed = false;//是否已经关闭
// Crypto is kind of old and crusty. Historically, its default string
// encoding is 'binary' so we have to make this configurable.
// Everything else in the universe uses 'utf8', though.
this.defaultEncoding = options.defaultEncoding || 'utf8';//设置Encoding
// the number of writers that are awaiting a drain event in .pipe()s
this.awaitDrain = 0;//在pipe中等待下一个管道触发的数量
// if true, a maybeReadMore has been scheduled
this.readingMore = false;//readable读取没达到最高水位线是否需要读取更多,知道缓存区满
//编码转换,解决乱码问题
this.decoder = null;//解码器
this.encoding = null;//编码
if (options.encoding) {
if (!StringDecoder)
StringDecoder = require('string_decoder').StringDecoder;
this.decoder = new StringDecoder(options.encoding);
this.encoding = options.encoding;
}
}
复制代码
下面是createReadStream创建读取流的源码 ———————— fs.js文件
//fs.createReadStream —— 1977行
fs.createReadStream = function(path, options) {
return new ReadStream(path, options);//首先返回ReadStream构造函数实例
};
util.inherits(ReadStream, Readable);//继承Readable
fs.ReadStream = ReadStream;
function ReadStream(path, options) {//传入文件路径,options参数
if (!(this instanceof ReadStream))//判断有没有new ReadStream()
return new ReadStream(path, options);
// a little bit bigger buffer and water marks by default
options = copyObject(getOptions(options, {}));//getOptions、copyObject函数源码如下
if (options.highWaterMark === undefined)
options.highWaterMark = 64 * 1024;//设置默认highWaterMark
Readable.call(this, options);//继承Readable,并且将options传入
handleError((this.path = getPathFromURL(path)));
this.fd = options.fd === undefined ? null : options.fd;//设置文件描述符
this.flags = options.flags === undefined ? 'r' : options.flags;//设置标识符是读取操作
this.mode = options.mode === undefined ? 0o666 : options.mode;//设置权限,默认0o666
this.start = options.start;//设置读取开始位置
this.end = options.end;//设置读取结束位置
this.autoClose = options.autoClose === undefined ? true : options.autoClose;//设置是否自动关闭
this.pos = undefined;//读取初始值
this.bytesRead = 0;//读取数量
this.closed = false;
if (this.start !== undefined) {
if (typeof this.start !== 'number') {//判断读取开始位置如果不是数值报错
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
'start',
'number',
this.start);
}
if (this.end === undefined) {
this.end = Infinity;//如果没有设置end,则无穷大
} else if (typeof this.end !== 'number') {//读取截至位置不是数值,报错
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
'end',
'number',
this.end);
}
if (this.start > this.end) {//如果读取开始位置大于读取结束位置,报错
const errVal = `{start: ${this.start}, end: ${this.end}}`;
throw new errors.RangeError('ERR_VALUE_OUT_OF_RANGE',
'start',
'<= "end"',
errVal);
}
this.pos = this.start;//让那个pos为读取开始位置判断后的正确结果
}
if (typeof this.fd !== 'number')
this.open();//如果fd不存在,说明文件没有打开,则调用打开方法,见下面open方法
this.on('end', function() {//监听end,如果触发end监听,判断为自动关闭为true则调用关闭函数destroy
if (this.autoClose) {
this.destroy();
}
});
}
//getOptions函数 —— 76行
function getOptions(options, defaultOptions) {//传入options和空对象默认值
if (options === null || options === undefined ||
typeof options === 'function') {//判断options不存在,或者是函数类型,返回给copyObject函数为空对象
return defaultOptions;
}
if (typeof options === 'string') {
//如果是options是字符串,defaultOptions的encoding为options,options为defaultOptions对象
defaultOptions = util._extend({}, defaultOptions);
defaultOptions.encoding = options;
options = defaultOptions;
} else if (typeof options !== 'object') {
//如果options为对象,则报错
throw new errors.TypeError('ERR_INVALID_ARG_TYPE',
'options',
['string', 'Object'],
options);
}
if (options.encoding !== 'buffer')
如果options.encoding 不是buffer,则转换
assertEncoding(options.encoding);
return options;
}
//copyObject函数 —— 98行
function copyObject(source) {
var target = {};
for (var key in source)
//遍历getOptions的返回值options
target[key] = source[key];
return target;//返回一个新对象
}
//open方法 —— 2046行
ReadStream.prototype.open = function() {
var self = this;//定义this
fs.open(this.path, this.flags, this.mode, function(er, fd) {//node fs.open API
if (er) {
if (self.autoClose) {//如果报错并且自动关闭为true,则调用关闭方法
self.destroy();
}
self.emit('error', er);//触发error,外部监听
return;
}
self.fd = fd;//打开成功,定义this.fd
self.emit('open', fd);//触发open监听,传入fd
// start the flow of data.
self.read();//开始流动模式,调用父类Readable read()方法,见下
});
};
复制代码
Readable ———— _stream_readable.js文件
//Readable read方法 —— 372行
Readable.prototype.read = function(n) {
debug('read', n);//debug
n = parseInt(n, 10);//n转为10进制取整
var state = this._readableState;//就是new ReadableState的实例,看上面Readable源码
var nOrig = n;//声明变量nOrig为传入的n
if (n !== 0)
state.emittedReadable = false;//如果n不为0,则让触发Readable为false
// if we're doing read(0) to trigger a readable event, but we
// already have a bunch of data in the buffer, then just trigger
// the 'readable' event and move on.
if (n === 0 &&
state.needReadable &&
(state.length >= state.highWaterMark || state.ended)) {
//如果n为0并且需要Readable并且,缓存区长度大于等于highWaterMark,则debuglog
debug('read: emitReadable', state.length, state.ended);
if (state.length === 0 && state.ended)
endReadable(this);//如果缓存区长度为0并且读取完毕,则触发endReadable函数,如下
else
emitReadable(this);//否则触发emitReadable ,如下
return null;//最后返回null
}
n = howMuchToRead(n, state);//执行howMuchToRead,如下
// if we've ended, and we're now clear, then finish it up.
if (n === 0 && state.ended) {//如果n为0并且读取结束
if (state.length === 0)//如果缓存区长度为0,则执行endReadable,如下
endReadable(this);
return null;//返回null
}
// All the actual chunk generation logic needs to be
// *below* the call to _read. The reason is that in certain
// synthetic stream cases, such as passthrough streams, _read
// may be a completely synchronous operation which may change
// the state of the read buffer, providing enough data when
// before there was *not* enough.
//
// So, the steps are:
// 1. Figure out what the state of things will be after we do
// a read from the buffer.
//
// 2. If that resulting state will trigger a _read, then call _read.
// Note that this may be asynchronous, or synchronous. Yes, it is
// deeply ugly to write APIs this way, but that still doesn't mean
// that the Readable class should behave improperly, as streams are
// designed to be sync/async agnostic.
// Take note if the _read call is sync or async (ie, if the read call
// has returned yet), so that we know whether or not it's safe to emit
// 'readable' etc.
//
// 3. Actually pull the requested chunks out of the buffer and return.
// if we need a readable event, then we need to do some reading.
var doRead = state.needReadable;//命名doRead为是否需要Readable
debug('need readable', doRead);//debuglog
// if we currently have less than the highWaterMark, then also read some
if (state.length === 0 || state.length - n < state.highWaterMark) {
doRead = true;//如果缓存区长度为0,或者缓存区长度减去n小于highWaterMark,则需要Readable
debug('length less than watermark', doRead);
}
// however, if we've ended, then there's no point, and if we're already
// reading, then it's unnecessary.
if (state.ended || state.reading) {
doRead = false;//如果读取结束,或者正在读取,则不需要Readable
debug('reading or ended', doRead);
} else if (doRead) {
debug('do read');
state.reading = true;//如果需要Readable,则正在读取
state.sync = true;//让emitReadable在下个事件环执行
// if the length is currently zero, then we *need* a readable event.
if (state.length === 0)//如果缓存区长度为0,则需要Readable
state.needReadable = true;
// call internal read method
this._read(state.highWaterMark);//然后调用子集方法_read,代码如下
state.sync = false;//让emitReadable在本次事件环
// If _read pushed data synchronously, then `reading` will be false,
// and we need to re-evaluate how much data we can return to the user.
if (!state.reading)//如果没有正在读取,则n等于howMuchToRead
n = howMuchToRead(nOrig, state);
}
var ret;
if (n > 0)
ret = fromList(n, state);//如果n大于0,则
else
ret = null;
if (ret === null) {
state.needReadable = true;//如果ret为null,则需要Readable
n = 0;
} else {
state.length -= n;//缓存区减去n
}
if (state.length === 0) {//如果缓存区长度为0
// If we have nothing in the buffer, then we want to know
// as soon as we *do* get something into the buffer.
if (!state.ended)//如果没有读取结束,则需要Readable
state.needReadable = true;
// If we tried to read() past the EOF, then emit end on the next tick.
if (nOrig !== n && state.ended)//如果nOrig不等于n或者读取结束,则调用endReadable,看endReadable代码
endReadable(this);
}
if (ret !== null)//如果ret不等于null,则触发data,流动模式,返回ret
this.emit('data', ret);
return ret;
};
// endReadable —— 1087行
function endReadable(stream) {
var state = stream._readableState;//state赋值为new ReadableState实例
if (!state.endEmitted) {//如果ended没有触发
state.ended = true;//让ended为true,读取完状态
process.nextTick(endReadableNT, state, stream);//下一个微观队列调用endReadableNT
}
}
function endReadableNT(state, stream) {
// Check that we didn't get one last unshift.
if (!state.endEmitted && state.length === 0) {//如果ended没有触发并且缓存区长度为0
state.endEmitted = true;//让endEmitted为true表示ended已经触发
stream.readable = false;//readable为false
stream.emit('end');//触发end监听
}
}
//emitReadable —— 505
function emitReadable(stream) {
var state = stream._readableState;//state赋值为new ReadableState实例
state.needReadable = false;//设置不需要readable
if (!state.emittedReadable) {//不触发readable事件
debug('emitReadable', state.flowing);//debuglog
state.emittedReadable = true;//让那emittedReadable为true,表示触发readable
if (state.sync)//sync为true,让emitReadable_为下个事件环(微观队列)
process.nextTick(emitReadable_, stream);
else
emitReadable_(stream);//否则直接触发emitReadable_
}
}
function emitReadable_(stream) {
debug('emit readable');//debuglog
stream.emit('readable');//触发readable监听
flow(stream);//执行flow
}
function flow(stream) {
const state = stream._readableState;//state赋值为new ReadableState实例
debug('flow', state.flowing);//debuglog
while (state.flowing && stream.read() !== null);//flowing为true流动模式并且read()不为null,循环read()
}
//howMuchToRead —— 346行
function howMuchToRead(n, state) {
if (n <= 0 || (state.length === 0 && state.ended))
return 0;//如果n小于等于0或者缓存区长度为0并且已经结束,则返回0
if (state.objectMode)
return 1;//如果处理的是对象流,返回1
if (n !== n) {
// Only flow one buffer at a time
if (state.flowing && state.length)//流动模式并且存在length
return state.buffer.head.data.length;//返回bufferList头部数据
else
return state.length;//否则返回缓存区大小
}
// If we're asking for more than the current hwm, then raise the hwm.
if (n > state.highWaterMark)//如果n大于highWaterMark
state.highWaterMark = computeNewHighWaterMark(n);//highWaterMark为computeNewHighWaterMark返回值,如下
if (n <= state.length)
return n;//如果n小于缓存区长度返回n
// Don't have enough
if (!state.ended) {//如果读取没有结束,则需要Readable返回0
state.needReadable = true;
return 0;
}
return state.length;//最后返回缓存区长度
}
// Don't raise the hwm > 8MB
const MAX_HWM = 0x800000;
function computeNewHighWaterMark(n) {
if (n >= MAX_HWM) {//如果n大于等于8M,则返回最大值8M,否则
n = MAX_HWM;
} else {
// Get the next highest power of 2 to prevent increasing hwm excessively in
// tiny amounts
n--;
n |= n >>> 1;
n |= n >>> 2;
n |= n >>> 4;
n |= n >>> 8;
n |= n >>> 16;
n++;
}
return n;//返回n的二进制形式
}
//fromList —— 974行
function fromList(n, state) {
// nothing buffered
if (state.length === 0)
return null;
var ret;
if (state.objectMode)
ret = state.buffer.shift();//如果是对象流,则ret等于第一个buffer
else if (!n || n >= state.length) {//如果n不存在或者n大于缓存区长度
// read it all, truncate the list
if (state.decoder)//如果解码存在
ret = state.buffer.join('');//将buffer拼接为数组
else if (state.buffer.length === 1)
ret = state.buffer.head.data;//如果buffer.length为1,则拿去bufferList头部数据
else
ret = state.buffer.concat(state.length);//否则将缓存区合并
state.buffer.clear();//清空buffer
} else {
// read part of list
ret = fromListPartial(n, state.buffer, state.decoder);//调用fromListPartial,如下
}
return ret;返回ret
}
function fromListPartial(n, list, hasStrings) {
var ret;
if (n < list.head.data.length) {//如果n小于bufferlist头部data的长度
// slice is the same for buffers and strings
ret = list.head.data.slice(0, n);//截取bufferList0-n数量
list.head.data = list.head.data.slice(n);//从新赋值bufferlist为n以后的
} else if (n === list.head.data.length) {
// first chunk is a perfect match
ret = list.shift();//如果n等于bufferlist头部data长度,则ret等于bufferList的第一个数据包括head data
} else {
// result spans more than one buffer
//如果n大于bufferList头部data长度,则判断state.decoder解码类型,字符串调用copyFromBufferString,buffer调用copyFromBuffer
ret = hasStrings ? copyFromBufferString(n, list) : copyFromBuffer(n, list);
}
return ret;//返回ret
}
function copyFromBufferString(n, list) {
var p = list.head;//p为bufferList的头部
var c = 1;
var ret = p.data;//ret为头部data
n -= ret.length;//n减去data长度
while (p = p.next) {
const str = p.data;
const nb = (n > str.length ? str.length : n);//如果n大于bufferList头部data长度,则n等于data长度否则为n
if (nb === str.length)
ret += str;//如果nb等于bufferList头部data长度,则ret加等于bufferList头部data
else
ret += str.slice(0, n);//否则加等于从0-n截取的头部data
n -= nb;
if (n === 0) {
if (nb === str.length) {
++c;
if (p.next)
list.head = p.next;
else
list.head = list.tail = null;
} else {
list.head = p;
p.data = str.slice(nb);
}
break;
}
++c;
}
list.length -= c;
return ret;//返回ret
}
function copyFromBuffer(n, list) {
const ret = Buffer.allocUnsafe(n);//创建一个n长度的buffer
var p = list.head;
var c = 1;
p.data.copy(ret);
n -= p.data.length;
while (p = p.next) {
const buf = p.data;
const nb = (n > buf.length ? buf.length : n);
buf.copy(ret, ret.length - n, 0, nb);
n -= nb;
if (n === 0) {
if (nb === buf.length) {
++c;
if (p.next)
list.head = p.next;
else
list.head = list.tail = null;
} else {
list.head = p;
p.data = buf.slice(nb);
}
break;
}
++c;
}
list.length -= c;
return ret;
}
复制代码
Fs.js文件
// _read —— 2064行
const kMinPoolSpace = 128;
ReadStream.prototype._read = function(n) {
if (typeof this.fd !== 'number') {//如果文件没打开,则监听一次open等待触发调用_read(n)
return this.once('open', function() {
this._read(n);
});
}
if (this.destroyed)//如果关闭,直接停止执行代码
return;
if (!pool || pool.length - pool.used < kMinPoolSpace) {
// discard the old pool.
//如果pool不存在,或者pool长度减去pool.used小于kMinPoolSpace,则调用allocNewPool,传入readableHighWaterMark,pool就是要真正读取的长度
allocNewPool(this.readableHighWaterMark);
}
// Grab another reference to the pool in the case that while we're
// in the thread pool another read() finishes up the pool, and
// allocates a new one.
var thisPool = pool;//thisPool为pool
var toRead = Math.min(pool.length - pool.used, n);//取最小,比较真正读取的长度和传入n长度
var start = pool.used;//start为used
if (this.pos !== undefined)//如果开始读取位置不存在,则toRead为结束减去开始+1,和上面toRead取最小
toRead = Math.min(this.end - this.pos + 1, toRead);
// already read everything we were supposed to read!
// treat as EOF.
if (toRead <= 0)
return this.push(null);//缓存区加入null
// the actual read.
//fs.read读取操作fd,数据将被写入到的buffer-pool,pool.used写入偏移量,toRead读取长度
fs.read(this.fd, pool, pool.used, toRead, this.pos, (er, bytesRead) => {
if (er) {
if (this.autoClose) {
this.destroy();//如果自动关闭为true,调用自动关闭函数,内部触发close
}
this.emit('error', er);//如果报错,触发error监听
} else {
var b = null;
if (bytesRead > 0) {//如果bytesRead读取长度大于0,this.bytesRead就加等于bytesRead,并且b等于写入缓存区长度的截取从开始位置到读取bytesRead的长度
this.bytesRead += bytesRead;
b = thisPool.slice(start, start + bytesRead);
}
this.push(b);//将b加入缓存区
}
});
// move the pool positions, and internal position for reading.
if (this.pos !== undefined)//如果开始位置不存在,则pos加等于toRead,并且pool.used加等于toRead
this.pos += toRead;
pool.used += toRead;
};
var pool;//1969行
function allocNewPool(poolSize) {
pool = Buffer.allocUnsafe(poolSize);//设置pool的buffer大小
pool.used = 0;
}
复制代码