Buffer
Buffer的構(gòu)成
Buffer對象類似數(shù)組,它的元素位16進制的兩位數(shù),即0到255的數(shù)值。主要是用來存儲二進制的數(shù)據(jù)
const buf = Buffer.from('Hello world')
初始化Buffer會隨機的填充了0到255的隨機值
var buffer = new Buffer(100);
buffer[20] = -100;
console.log(buffer[20]) // 如果存儲的數(shù)字小于0或者大于255則直接加值256知道值位于0到255之間
Buffer的底層分配
Node從c++層面申請內(nèi)存,在JS層面用來分配內(nèi)存的策略,Buffer在C++申請內(nèi)存主要是8K。
Buffer的轉(zhuǎn)換
目前Buffer的對象是可以直接與字符串進行互相轉(zhuǎn)換,目前支持的字符串編碼類型有
ASCII
UTF-8
UTF-16
Base64
Binary
-
Hex
目前官方暫時不支持漢字編碼GBK之類,如果有類似的漢字編碼需求需要調(diào)用第三方庫的支持。
Buffer的亂碼現(xiàn)象
因為Buffer對象只要是用于二進制與字符串的轉(zhuǎn)換,一旦設(shè)計編碼 解碼就會出現(xiàn)所謂的亂碼現(xiàn)象,特別是出現(xiàn)在漢字編碼問題。
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,主要是影響操作系統(tǒng)讀取文件的字節(jié)塊,目前是按照11字節(jié)讀取,因為漢字在UTF-8中編碼只要是三個字節(jié),所以在讀取漢字的時候可能會出現(xiàn)兩個字節(jié)導(dǎo)致無法編碼,出現(xiàn)所謂的亂碼現(xiàn)象。
亂碼解決方案
- stream.setEncoding('utf-8') 這樣當(dāng)stream讀取到中文漢字需要解碼的時候 底層的string_decoder 則會收集完整的字節(jié)然后解碼顯示漢字
- 通過將stream中的數(shù)據(jù)接受完整數(shù)據(jù)后,然后指定編碼符來統(tǒng)一解碼。
Stream
Node.js中有四種基本的流類型:
- Readable 可讀的流(例如 fs.createReadStream())
- Writable 可寫的流 (例如 fs.createWriteStream())
- Duplex 可讀寫的流 ( 例如 net.Socket)
- Transform 在讀寫過程中科院修改和變換數(shù)據(jù)的Duplex流
緩沖
Writable和Readable流都會將數(shù)據(jù)存儲到內(nèi)部的緩存中。這些緩存可以通過相應(yīng)的writable._writableState.getBuffer()或者readable._readableState.buffer來獲取。 緩存的大小取決流構(gòu)造函數(shù)的highWaterMark選項。對于普通的流, highWaterMark選項指定了總共的字節(jié)數(shù)。對于工作在對象模式的流,highWaterMark指定了對象的總數(shù)。
- 當(dāng)可讀流實現(xiàn)調(diào)用了stream.push(chunk)方法,數(shù)據(jù)被放到了緩存中,如果流的消費者沒有調(diào)用stream.read()方法,這些數(shù)據(jù)會始終存在內(nèi)部隊列,直到被消費。
- 可寫流通過反復(fù)調(diào)用writable.write(chunk)方法將數(shù)據(jù)放到緩存。當(dāng)內(nèi)部可寫入緩存的總大小小于highWaterMark指定的閥值,調(diào)用writable.write()將返回true.
- streamAPI的關(guān)鍵方法在于stream.pipe()方法,就是限制緩存數(shù)據(jù)大小,以達到可接受的程度。這樣,對于讀寫速度不匹配的源頭和目標(biāo)。
可寫流
Write Streams 是destination的一種抽象,這種destination允許數(shù)據(jù)寫入
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事件 底層資源關(guān)閉后觸發(fā) close事件觸發(fā)后,該流將不會再觸發(fā)任何事件
- drain 事件 當(dāng)stream.write(chunk)方法返回false 出發(fā) 此時才能繼續(xù)讓緩沖區(qū)寫入數(shù)據(jù)
- error事件 stream寫入數(shù)據(jù)出錯
- finish 事件 stream.end()方法
- pipe 事件 輸出到目標(biāo)可寫入流的源流 stream.pipe() 方法
- writable.cork() 強制將所有寫入數(shù)據(jù)源都放入內(nèi)存中的緩沖區(qū)
- 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模式下,可讀流自動從系統(tǒng)底層讀取數(shù)據(jù),并通過EventEmitter接口的事件盡快將數(shù)據(jù)提供給應(yīng)用
在paused模式下,必須顯示調(diào)用stream,read()方法來從流中讀取數(shù)據(jù)片段
所有初始工作位paused的Readable流,可以通過下面三種途徑切換到flowing模式:
- 監(jiān)聽了'data'事件
- 調(diào)用了stream.resume()方法
- 調(diào)用了stream.pipe()方法將數(shù)據(jù)發(fā)送Writable
從Flowing模式切換到paused模式
- 如果存在了管道目標(biāo),可以通過取消'data'事件箭筒,并調(diào)用stream.unpipe()方法移除所有管道目標(biāo)來實現(xiàn)
- 如果不存在管道目標(biāo),直接調(diào)用stream.pause()方法來實現(xiàn)
三種狀態(tài)
任意可讀流應(yīng)確切處于下面三種狀態(tài):
-
readable._readableState.flowing = null
由于不存在數(shù)據(jù)消費者,可讀流將不會產(chǎn)生數(shù)據(jù)
-
readable._readableState.flowing = false
調(diào)用
readable.pause()
方法,readable.unpipe()
方法, 或者接收 “背壓”(back pressure), 將導(dǎo)致readable._readableState.flowing
值變?yōu)?false
。 這將暫停事件流,但 不會 暫停數(shù)據(jù)生成。 -
readable._readableState.flowing = true
當(dāng)stream監(jiān)聽了 'data'事件 調(diào)用了pipe方法,或者調(diào)用readable.resume()方法