Stream是Node.js中非常重要的一個模組,應用廣泛。
先了解什麼是流
流是一組有序的,有起點和終點的位元組資料傳輸手段,是一個抽象介面,從而實現了從一個地方流到另一個地方。
Stream 有四種流型別:
- Readable - 可讀的流 (例如fs.createReadStream()).
- Writable - 可寫的流 (例如fs.createWriteStream()).
- Duplex - 雙工流 (可讀寫的流)(例如 net.Socket).
- Transform - 變換流:在讀寫過程中可以修改和變換資料的 Duplex 流 (例如zlib.createDeflate()).
下面就一一介紹Stream的四種型別:
可讀流(Readable streams)
Readable流提供了一種將外部來源的資料讀入到應用程式的機制。
可讀的流有兩種模式:flowing (流動模式)和 paused(暫停模式)
在 flowing 模式下,可讀流自動從系統底層讀取資料,並通過 EventEmitter 介面的事件儘快將資料提供給應用,直到來源的資料耗盡
在 paused 模式下,必須顯式呼叫 stream.read() 方法來從流中讀取資料片段, 你不要它就在那兒耗著等你。暫停模式是一個可讀流例項被建立時的預設模式
暫停模式和流動模式可以互相轉換。
所有初始工作模式為 paused 的 Readable 流,可以通過下面三種途徑切換到 flowing 模式:
- 監聽 'data' 事件
- 呼叫 stream.resume() 方法
- 呼叫 stream.pipe() 方法將資料傳送到 Writable
可讀流可以通過下面途徑切換到paused 模式:
- 如果不存在管道目標(pipe destination),可以通過呼叫 stream.pause() 方法實現。
- 如果存在管道目標,可以通過取消'data' 事件監聽,並呼叫 stream.unpipe() 方法移除所有管 道目標來實現。
所有的流都是是基於事件eventEmitter,通過發射事件來反饋流的狀態。 流的傳輸過程預設是以buffer的形式傳輸的,除非你給他設定其他編碼形式.
我們來舉一個簡單的Readable的例子。
/**可讀流**/
//首先建立一個1.txt檔案,裡面寫有1234567890的字元
let fs = require('fs');//引入一個fs檔案
//建立一個可讀流
let rs = fs.createReadStream('./1.txt',{
flags:'r',//我們要對檔案進行何種操作,r是讀檔案
encoding:'utf8',//設定編碼
start:0,//從索引為0的位置開始讀
end:7,//讀到索引為8結束
highWaterMark:3// highWaterMark緩衝區大小,highWaterMark快取區預設大小64kb
});
//先觸發檔案開啟事件
rs.on('open',function () {
console.log('檔案開啟');
});
//監聽它的data事件,當你一旦開始監聽data事件的時候,流就可以讀檔案的內容並且發射data
//預設情況下,當你監聽data事件之後,會不停的讀資料,然後觸發data事件,
//觸發完data事件後再次讀資料
rs.on('data',function (data) {
console.log(data);
rs.pause();//暫停讀取和發射data事件
setTimeout(function(){
rs.resume();//一秒後恢復讀取並觸發data事件,直到檔案讀取完了。
},1000);
});
//將在流中有資料可供讀取時觸發
rs.on('readable',function(){
//讀取一個欄位,如果可讀流發現你要讀的位元組小於等於快取位元組大小,則直接返回
//當你讀完指定的位元組後,如果可讀流發現剩下的位元組已經比最高水位線小了。
//則會立馬再次讀取填滿 最高水位線
let char = rs.read(1);
console.log(char);
});
//如果讀取檔案中出錯了,會觸發error事件
rs.on('error',function (err) {
console.log(err);
});
//如果檔案的內容讀完了,會觸發end事件
rs.on('end',function () {
console.log('讀完了');
});
//如果檔案的內容讀完了,會觸發close事件
rs.on('close',function () {
console.log('檔案關閉');
});
/*輸出結果
檔案開啟
123
4
4
56
7
7
8
讀完了
檔案關閉*/
複製程式碼
注:readable:在資料塊可以從流中讀取的時候發出。它對應的處理器沒有引數,可以在處理器裡呼叫read([size])方法讀取資料。
read([size]):該方法可以接受一個整數作為引數,表示所要讀取資料的數量,然後會返回該數量的資料。如果讀不到足夠數量的資料,返回null。如果不提供這個引數,預設返回系統快取之中的所有資料。
可寫流(Writable streams)
實現了stream.Writable介面的物件來將流資料寫入到物件中
Writable流的write(chunk[,encoding] [,callback])方法可以把資料寫入流中。
chunk是我們即將要寫入的資料,通常是一個buffer或String物件
encoding設定指定字串的編碼格式
callback是在完成處理資料塊後需要呼叫的函式。這是寫資料成功與否的標誌。若要發出故障訊號,請用錯誤物件呼叫回撥函式
write方法返回布林值,預設是true,代表當前緩衝區(highWaterMark)的狀態,如果緩衝區的狀態已經到最高水位線,write
方法會返回false。一旦我們確認方法返回了false後,應該立刻停止呼叫write
方法,直到緩衝器中的資料被清空為止。此時會呼叫 'drain' 事件,在 'drain' 事件觸發前, 不能寫入任何資料塊。
我們來舉一個簡單的Writable的例子。
let fs = require('fs');
//建立一個可寫流
let ws = fs.createWriteStream('./1.txt',{
flags:'w',//我們要對檔案進行何種操作
mode:0o666,
autoClose:true,//是否自動關閉檔案
encoding:'utf8',//設定編碼
start:0,//從第個索引開始寫
highWaterMark:3// highWaterMark緩衝區大小,highWaterMark讀取快取區預設的大小64kb
});
let i = 6;
function write(){
//如果快取區已滿 ,返回false,如果快取區未滿,返回true
//如果能接著寫,返回true,如果不能接著寫,返回false
//按理說如果返回了false,就不能再往裡面寫了,但是如果你真寫了,也不會丟失,會快取在記憶體裡。
//等快取區清空之後再從記憶體裡讀出來
let flag = true;
while(flag && i>0){
flag = ws.write((i--)+'');
console.log('flag',flag);
console.log(i);
}
}
write();
ws.on('error',function () {
console.error('error');
});
ws.on('drain',function () {
console.log('drain');
write();
});
/*輸出結果
flag true
5
flag true
4
flag false
3
drain
flag true
2
flag true
1
flag false
0
drain
*/
複製程式碼
小編先寫到這裡,後續在給大家介紹另兩種方法
擴充套件