簡單介紹流
一般處理數(shù)據(jù)有兩種模式, buffer模式和stream模式, buffer模式就是取完數(shù)據(jù)一次性操作, stream模式就是邊取數(shù)據(jù)邊操作.
例如如果打開一個8G的文件, 用buffer模式就是先分配2G的內(nèi)存, 把文件全部讀出來, 然后開始操作內(nèi)存, 而用流模式的方法就是邊讀數(shù)據(jù), 邊開始處理.
從這里看出stream模式無論是在空間和時間上都優(yōu)于buffer模式:在空間上, 內(nèi)存只會占用當(dāng)前需要處理的一塊數(shù)據(jù)區(qū)域的大小, 而不是整個文件.在時間上, 因為不需要全部的數(shù)據(jù)就可以開始處理, 時間就相當(dāng)于節(jié)約了, 從串行變成了并行操作(這里的并行不是多線程的并行, 而是生產(chǎn)者和消費者并行).
美團(tuán)在流的進(jìn)階篇中就有介紹其作用的一個實例
const fs = require('fs');
fs.readFile(file, function (err, body) {
????????console.log(body);
????????console.log(body.toString());
})
然后如果文件過大就會出現(xiàn)如下情況

報錯的原因是body這個Buffer對象的長度過大,導(dǎo)致toString方法失敗。
可見,這種一次獲取全部內(nèi)容的做法,不適合操作大文件。
可以考慮使用流來讀取文件內(nèi)容。
const fs = require('fs');
fs.createReadStream(file).pipe(process.stdout);
fs.createReadStream創(chuàng)建一個可讀流,連接了源頭(上游,文件)和消耗方(下游,標(biāo)準(zhǔn)輸出)。
還有一個好處就是鏈?zhǔn)秸{(diào)用, 也就是可組合操作, 大大增加了代碼的可重用性.比如下面這個代碼(中間的pipe可以很方便的增刪):
fs.createReadStream(file)
????????.pipe(zlib.createGzip())
????????//.pipe(crypto.createCipher('aes192', 'secret'))
????????.pipe(req) .on('finish', function() {
????????console.log('File succesfully sent');
});??
深入了解
后面的介紹會涉及到下面的3各方面,今天只詳細(xì)解讀第一部分
1.流的基本類型,以及Stream模塊的基本使用方法
2.流式處理與back pressure的工作原理
3.如何開發(fā)流式程序,包括對Gulp與Browserify的剖析,以及一個實戰(zhàn)示例。
流的四種類型
Stream提供了以下四種類型的流:
var? Stream =require('stream')
var? Readable = Stream.Readable
var? Writable = Stream.Writable
var? Duplex = Stream.Duplex
var? Transform = Stream.Transform
使用Stream可實現(xiàn)數(shù)據(jù)的流式處理,如:
var fs = require('fs')
// `fs.createReadStream`創(chuàng)建一個`Readable`對象以讀取`bigFile`的內(nèi)容,并輸出到標(biāo)準(zhǔn)輸出
// 如果使用`fs.readFile`則可能由于文件過大而失敗
fs.createReadStream(bigFile).pipe(process.stdout)
Readable:創(chuàng)建可讀流。
Writable:創(chuàng)建可寫流。
Duplex:創(chuàng)建可讀可寫流。
Transform:在Transform中可寫端寫入的數(shù)據(jù)經(jīng)變換后會自動添加到可讀端。
(詳細(xì)例子可以參考如下流-基礎(chǔ)篇-美團(tuán))
今天先到這里,后面第二篇會詳細(xì)講解到流背后的工作原理,以及如何實現(xiàn)流式數(shù)據(jù)處理和back presure機制
參考: