簡單認識和使用node 中的流(stream)

junyu-node發表於2018-07-16

為什麼要用流

- 如果整體讀寫, 檔案比較大 ,一次性讀取就會佔用大量記憶體,效率低下,而且記憶體是非常珍貴的,所有就有了流的誕生。
- 流是將資料分隔段 ,一段一段的讀取,效率很高
複製程式碼

流是什麼

- 流是一組有序的 油漆店和終點的位元組資料傳輸手段
- 他不關心整體的內容 只關注是否從檔案中讀到了資料 以及杜德奧資料之後的處理
- 流(stream)在node.js中是一個抽象介面 被node中的很多物件所實現
- 流是可讀 可寫的 所有的流都是EventEmitter 的例項
複製程式碼

流的分類

- 可讀流(Readable Stream)
- 可寫流(Writeable Stream)
- 雙工流(Duplex Stream)
- 轉換流(Transform Stream)
複製程式碼

知道了一些流的基本情況 那麼我們就來了解一下怎麼使用流

可讀流

- 首先先看一下可讀流的api
複製程式碼
    let rs=fs.createReadStream(path,options) //建立一個可讀流
    /*
    * path 讀取檔案的路勁
    * options 引數
    *   - flags 開啟檔案要做的操作 預設為 ‘r’
    *   - encoding 讀取檔案的編碼 預設為 null
    *   - start 開始讀取的索引位置
    *   - end 結束讀取的索引位置 (含結束的位置)
    *   - highWaterMark 讀取快取預設的大小  預設值為64K 1024*64
    */  
複製程式碼

Readbale 的事件

- data事件 資料正在傳遞是 觸發該事件
- end 事件 資料傳遞完成後 觸發事件
- open 時間 開啟檔案
- close 事件  關閉檔案 觸發事件
- error 報錯後觸發事件
- rs.pause rs.resume  暫停和恢復觸發
複製程式碼

配合可讀流的api和事件 我們簡單些一個小例子

    //1.txt 內容 123你好世界        
    let fs =require('fs');
    let rs=fs.createReadStream('./1.txt',{
        flags:'r',
        start:3,
        end:10,
        highWaterMark:3
    })
    
    rs.on('open',(data)=>{
        console.log('開啟檔案')
    })
    //監聽data事件
    rs.on('data',(data)=>{
        console.log(data)
    })
    
    //結果 //<Buffer e4 bd a0>   //.toString() 你
        //<Buffer e5 a5 bd> //.toString() 好
        //<Buffer e4 b8> 
        
    //結果分析  首先  flags 是 r 所以是可讀  start:3  就是從第三個開始 highWaterMark:3 每次讀三個 然後 end : 10  一共讀 10個   所以 第一次 讀 的正好是 ‘你’ 第二次 是 ’好‘
    第三次是中文轉的buffer的兩個位元組 共(10-3+1)8個位元組
    
    //監聽end事件
    rs.on('end',(data)=>{
        console.log('結束')
    })
    // 結果//結束
    
     rs.on('close',(data)=>{
        console.log('關閉')
    })
    rs.setEncoding('utf8'); //設定編碼 等同{encoding:'utf8'}
    
    在 data事件的時候可以觸發暫停事件  
    re.on('data',(data)=>{
        res.pause() // 如果是上邊用了暫停的話只會觸發一次 consosle.log(data)
    })
    
    rs.resyme() //恢復觸發data事件

複製程式碼

可寫流

-還是看一下他的api
複製程式碼
let ws=fs.createWriteStream(path,options)

/*
    path 寫檔案的路勁
    options
        flages 開啟檔案要做的操作 預設為w
        encoding 預設為utf8
        hifgWaterMark 寫入快取區的大小 預設16kb 
*/


複製程式碼

Writeable 的方法

- write 
- end
- drain
- finish
複製程式碼

ws.write(chunk,encoding,callback)
ws.end(chunk,encoding,callback)

/*
chunk  寫入的內容 buffer 或者string
encoding 寫入的編碼 預設utf8
callback回撥

*/


複製程式碼

drain 方法 (中文的意思是排水,排幹)

- drain 方法是監測寫入的內容是不是在記憶體中還存在  只有記憶體中不存在寫入的內容才觸發 drain 方法  
複製程式碼

舉一個例子

let fs=require('fs');
let ws=fs.createWriteStream('2.txt',{
    falgs:'w',
    highWaterMark:3
})

let i=10
function write(){
    let flag=true;
    while(i&&flag){
        flag=ws.write(i+"");
        i--;
        console.log(flag)
    }
}
write();

ws.on('drain',()=>{
    console.log('排幹');
    write();
})
//一共執行    排幹 3次
複製程式碼

finish 方法

在呼叫了 stream.end() 方法,且緩衝區資料都已經傳給底層系統之後, 'finish' 事件將被觸發。

還是上邊那段程式碼 然後後邊加上

ws.end('結束');

ws.on('finish',()=>{
    console.log(111);
})

//  排幹 沒有被執行  2.txt 裡邊是  109結束

 //執行結果是  true  false 111
 

複製程式碼

上邊就是讀和寫流的方法 那麼我們接著往下介紹

pipe 的原理 和用法

  • gulp很多都用了這個方法
//原理
/*
        從一個檔案讀取,到另一個檔案
        
        從一個檔案讀取部分(1.txt),讀取的部分寫入到另一個檔案(2.txt);
        讀取的一部分在記憶體上如果沒有寫完 就等待讀取,等什麼時候寫完再繼讀 寫 直到1的檔案內容讀到2檔案內 關閉 檔案
        

*/
let fs= require('fs');
let rs=fs.createReadStream('./1.txt');
let ws=fs.createWriteStream('./2.txt');
rs.on('data',(data)=>{
    var flag=ws.write(data);
    if(!flag){
        rs.pause();
    }
});
ws.on('drain',()=>{
    rs.resume();
})
rs.on('end',()=>{
    ws.end();
})


// 使用

rs.pipe(ws);



複製程式碼

Duplex

Duplex 雙工流 簡單實現

```

let {Duplex} = require('stream');

class MyDuplex extends Duplex{
     _read(){
   
    }
    _write(chunk,encoding,callback){
        
        callback();
    }
}
```
複製程式碼

Transform 轉換流

簡單實現 
複製程式碼
    let {Transform} = require('stream');
    class MyTransform extends Transform{
        _transform(chunk,encoding,callback){
            console.log(chunk.toString().toUpperCase());
            callback();
            this.push('123');
        }
    }
    let myTransform = new MyTransform();
    
    process.stdin.pipe(myTransform).pipe(process.stdout);
複製程式碼

以上就是stream的簡單介紹,我就寫到這裡,下一節,我們來自己實現一個 readStream 和writeStream

相關文章