Buffer
Buffer的构成
Buffer对象类似数组,它的元素位16进制的两位数,即0到255的数值。主要是用来存储二进制的数据
const buf = Buffer.from('Hello world')
初始化Buffer会随机的填充了0到255的随机值
var buffer = new Buffer(100);
buffer[20] = -100;
console.log(buffer[20]) // 如果存储的数字小于0或者大于255则直接加值256知道值位于0到255之间
Buffer的底层分配
Node从c++层面申请内存,在JS层面用来分配内存的策略,Buffer在C++申请内存主要是8K。
Buffer的转换
目前Buffer的对象是可以直接与字符串进行互相转换,目前支持的字符串编码类型有
ASCII
UTF-8
UTF-16
Base64
Binary
-
Hex
目前官方暂时不支持汉字编码GBK之类,如果有类似的汉字编码需求需要调用第三方库的支持。
Buffer的乱码现象
因为Buffer对象只要是用于二进制与字符串的转换,一旦设计编码 解码就会出现所谓的乱码现象,特别是出现在汉字编码问题。
var rs = fs.createReadStream('buffer.txt',{
highWaterMark:11
});
var data = '';
rs.on('data',(thunk)=>{
data +=thunk; // data.toString() += thunk.toString()
})
rs.on('end',()=>{
console.log(data);
})
在Options上选择了highWaterMark,主要是影响操作系统读取文件的字节块,目前是按照11字节读取,因为汉字在UTF-8中编码只要是三个字节,所以在读取汉字的时候可能会出现两个字节导致无法编码,出现所谓的乱码现象。
乱码解决方案
- stream.setEncoding('utf-8') 这样当stream读取到中文汉字需要解码的时候 底层的string_decoder 则会收集完整的字节然后解码显示汉字
- 通过将stream中的数据接受完整数据后,然后指定编码符来统一解码。
Stream
Node.js中有四种基本的流类型:
- Readable 可读的流(例如 fs.createReadStream())
- Writable 可写的流 (例如 fs.createWriteStream())
- Duplex 可读写的流 ( 例如 net.Socket)
- Transform 在读写过程中科院修改和变换数据的Duplex流
缓冲
Writable和Readable流都会将数据存储到内部的缓存中。这些缓存可以通过相应的writable._writableState.getBuffer()或者readable._readableState.buffer来获取。 缓存的大小取决流构造函数的highWaterMark选项。对于普通的流, highWaterMark选项指定了总共的字节数。对于工作在对象模式的流,highWaterMark指定了对象的总数。
- 当可读流实现调用了stream.push(chunk)方法,数据被放到了缓存中,如果流的消费者没有调用stream.read()方法,这些数据会始终存在内部队列,直到被消费。
- 可写流通过反复调用writable.write(chunk)方法将数据放到缓存。当内部可写入缓存的总大小小于highWaterMark指定的阀值,调用writable.write()将返回true.
- streamAPI的关键方法在于stream.pipe()方法,就是限制缓存数据大小,以达到可接受的程度。这样,对于读写速度不匹配的源头和目标。
可写流
Write Streams 是destination的一种抽象,这种destination允许数据写入
Writable的例子包括了
- HTTP request, on the client
- HTTP response, on the server
- fs write streams
- lib streams
- crypto streams
- TCP sockets
- child process stdin
Writeable流暴露了一些方法 比如write() end() pipe()方法
Writable类的事件和方法
- close事件 底层资源关闭后触发 close事件触发后,该流将不会再触发任何事件
- drain 事件 当stream.write(chunk)方法返回false 出发 此时才能继续让缓冲区写入数据
- error事件 stream写入数据出错
- finish 事件 stream.end()方法
- pipe 事件 输出到目标可写入流的源流 stream.pipe() 方法
- writable.cork() 强制将所有写入数据源都放入内存中的缓冲区
- writable.end(chunk, encoding, callback)
- writable.setDefaultEncoding
- writable.write(chunk,encoding,callback)
- writable.destroy(error)
可读流
Readable的例子
- HTTP response, on the client
- HTTP request , on the server
- fs read streams
- zlib streams
- crypto streams
- TCP sockets
- child process stdin
可读流的时间和方法
- close事件
- data事件
- end事件
- error事件
- readable.pause()
- readable.pipe()
- readable.unpipe()
- readable.setEncoding(encoding)
- readable.destory(error)
两种模式
可读流事实上工作在下面两种模式之一: flowing 和paused
在flowing模式下,可读流自动从系统底层读取数据,并通过EventEmitter接口的事件尽快将数据提供给应用
在paused模式下,必须显示调用stream,read()方法来从流中读取数据片段
所有初始工作位paused的Readable流,可以通过下面三种途径切换到flowing模式:
- 监听了'data'事件
- 调用了stream.resume()方法
- 调用了stream.pipe()方法将数据发送Writable
从Flowing模式切换到paused模式
- 如果存在了管道目标,可以通过取消'data'事件箭筒,并调用stream.unpipe()方法移除所有管道目标来实现
- 如果不存在管道目标,直接调用stream.pause()方法来实现
三种状态
任意可读流应确切处于下面三种状态:
-
readable._readableState.flowing = null
由于不存在数据消费者,可读流将不会产生数据
-
readable._readableState.flowing = false
调用
readable.pause()
方法,readable.unpipe()
方法, 或者接收 “背压”(back pressure), 将导致readable._readableState.flowing
值变为false
。 这将暂停事件流,但 不会 暂停数据生成。 -
readable._readableState.flowing = true
当stream监听了 'data'事件 调用了pipe方法,或者调用readable.resume()方法