Node.js開發入門—Stream(流)基本用法詳解

yingzai發表於2018-02-03

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 模式:

  1. 監聽 'data' 事件
  2. 呼叫 stream.resume() 方法
  3. 呼叫 stream.pipe() 方法將資料傳送到 Writable

可讀流可以通過下面途徑切換到paused 模式:

  1. 如果不存在管道目標(pipe destination),可以通過呼叫 stream.pause() 方法實現。
  2. 如果存在管道目標,可以通過取消'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
*/
複製程式碼

小編先寫到這裡,後續在給大家介紹另兩種方法

擴充套件

Nodejs Stream 資料流使用手冊


相關文章