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()方法