node.js中的流

context發表於2018-02-11
node.js中的流是一種資料傳輸手段,流是有順序的。流不關心整體流程,只管取出資料,獲取資料後的操作。
流有四種基本的型別
Readable - 可讀的流 (例如 fs.createReadStream()). 
Writable - 可寫的流 (例如 fs.createWriteStream()).
Duplex - 可讀寫的流 (例如 net.Socket). 
Transform - 在讀寫過程中可以修改和變換資料的 Duplex 流 (例如 zlib.createDeflate()).
可讀流有兩種模式
flowing 流動模式
paused 暫停模式

流動模式:flowing 沒有快取區。讀一點資料,發射一點資料。當資料全部讀完了觸發一個end事件。例如:pipe(),resume()方法不走快取.
data事件,當你一旦開始監聽data事件的時候,流就可以讀檔案的內容並且發射data 。預設請況下,當你監聽data事件之後,會不停的讀資料,然後觸發data事件,觸發完data事件後再次讀資料。

let rs=fs.createReadStream('./11.txt',{
    highWaterMark:3
});
rs.setEncoding('utf8');rs.on('data',function(data){
    //data獲取到的是個buffer,要想獲取字元需要設定編碼
    console.log(data);
});
rs.on('end',function(){
    console.log('檔案讀完了');
});複製程式碼

pipe是可讀流 的方法

ReadStream.prototype.pipe = function (dest) {
    this.on('data', (data)=>{
        let flag = dest.write(data);//寫入資料,返回true,說明快取區沒滿還可以繼續寫。返回
        false暫停一下。監聽drain事件,等到觸發drain事件說明資料消化完了,再繼續讀取資料

        if(!flag){
            this.pause();
        }
    });
    dest.on('drain', ()=>{
        this.resume();
    });
    this.on('end', ()=>{
        dest.end();
    });
}
ReadStream.prototype.pause = function(){
    this.flowing = false;

}
ReadStream.prototype.resume = function(){
    this.flowing = true;
    this.read();
}
複製程式碼

dest 資料寫入目標
可以在單個可讀流上繫結多個可寫流。
複製程式碼

const r = fs.createReadStream('file.txt');
const z = zlib.createGzip();
const w = fs.createWriteStream('file.txt.gz');
r.pipe(z).pipe(w);複製程式碼

暫停模式:paused (初始化模式) 內部設定一個快取區,快取區預設大小64kb.實際大小以highWaterMark的值為準。當你監聽 readable事件的時候,會進入暫停模式。讀取highWaterMark的值放入快取區,觸發readable事件。

let fs = require('fs');
let rs = fs.createReadStream('./1.txt',{
    highWaterMark:3
});
rs.on('readable',()=>{
   onsole.log(rs._readableState.length);//3
   let ch = rs.read(1);
   //當你讀了一個位元組後,發現只剩下2個位元組,不夠highWaterMark,會再次讀取highWaterMark個位元組並填到
快取區內
       console.log(rs._readableState.length);//2
       let ch = rs.read(3);
       setTimeout(()=>{
           console.log(rs._readableState.length);//5
       },200)
});
複製程式碼


可寫流就是往裡面寫資料

當你往可寫流裡寫資料的時候,不是會立刻寫入檔案的,而是會先寫入快取區,快取區的大小就是highWaterMark的值,預設是16k。 然後等快取區滿了之後再次真正的寫入檔案裡。

let fs=require('fs');
let ws=fs.createWriteStream('22.txt',{
    flags:'w',
    mode:0o666,
    start:0,
    highWaterMark:3
});
//如果快取區已滿,返回false.如果快取區沒滿返回true.
//如果能接著寫,返回true.如果不能寫返回false.
//按理說如果返回了false,就不能再往裡面寫了。但是如果繼續往裡面寫,也不會丟失,會快取在記憶體裡。
等快取區清空之後再從記憶體裡讀出來let flag=ws.write('1');
console.log(flag);//true

flag=ws.write('2');
console.log(flag);//true

flag=ws.write('3');
console.log(flag);//false複製程式碼
自定義流
let {Writable,Readable,Duplex,Transform} = require('stream');複製程式碼
自定義可讀流
為了實現可讀流,引用Readable介面並用它構造新物件。
  • 我們可以直接把供使用的資料push出去。
  • 當push一個null物件就意味著我們想發出訊號——這個流沒有更多資料了。


var stream = require('stream');
var util = require('util');
util.inherits(Counter, stream.Readable);
function Counter(options) {
    stream.Readable.call(this, options);
    this._index = 0;
}
Counter.prototype._read = function() {
    if(this._index++<3){
        this.push(this._index+'');
    }else{
        this.push(null);
    }
};
var counter = new Counter();

counter.on('data', function(data){
    console.log("讀到資料: " + data.toString());//no maybe
});
counter.on('end', function(data){
    console.log("讀完了");
});
複製程式碼

自定義可寫流

為了實現可寫流,我們需要使用流模組中的Writable建構函式。 我們只需給Writable建構函式傳遞一些選項並建立一個物件。唯一需要的選項是write函式,該函式揭露資料塊要往哪裡寫。

  • chunk通常是一個buffer,除非我們配置不同的流。
  • encoding是在特定情況下需要的引數,通常我們可以忽略它。
  • callback是在完成處理資料塊後需要呼叫的函式。這是寫資料成功與否的標誌。若要發出故障訊號,請用錯誤物件呼叫回撥函式

var stream = require('stream');
var util = require('util');
util.inherits(Writer, stream.Writable);
let stock = [];
function Writer(opt) {
    stream.Writable.call(this, opt);
}
Writer.prototype._write = function(chunk, encoding, callback) {
    setTimeout(()=>{
        stock.push(chunk.toString('utf8'));
        console.log("增加: " + chunk);
        callback();
    },500)
};
var w = new Writer();
for (var i=1; i<=5; i++){
    w.write("專案:" + i, 'utf8');
}
w.end("結束寫入",function(){
    console.log(stock);
});
複製程式碼

雙工流
雙工流(可讀可寫流)是可讀流可寫流的實現。例如:net.Socket

let {Duplex} = require('stream');
let index = 0;
let s = Duplex({
    read(){
        if(index++<3)
          this.push('a'); 
          else 
       this.push(null);   
    },
    write(chunk,encoding,cb){
       console.log(chunk.toString().toUpperCase());
       cb();
    }
});
//process.stdin 標準輸入流
//proces.stdout標準輸出流
process.stdin.pipe(s).pipe(process.stdout);複製程式碼

Transform轉換流
轉換流是實現資料轉換的,(可讀流可寫流)只能實現一種。

let {Transform}  = require('stream');
let t = Transform({
    transform(chunk,encoding,cb){
        this.push(chunk.toString().toUpperCase());
        cb();
    }
});
process.stdin.pipe(t).pipe(process.stdout);
複製程式碼


相關文章