為什麼要用流
- 如果整體讀寫, 檔案比較大 ,一次性讀取就會佔用大量記憶體,效率低下,而且記憶體是非常珍貴的,所有就有了流的誕生。
- 流是將資料分隔段 ,一段一段的讀取,效率很高
複製程式碼
流是什麼
- 流是一組有序的 油漆店和終點的位元組資料傳輸手段
- 他不關心整體的內容 只關注是否從檔案中讀到了資料 以及杜德奧資料之後的處理
- 流(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