javascript的一個不足之處是不能處理二進制數據,於是node中引入了Buffer類型。這個類型以一個字節(即8位)為單位,給數據分配存儲空間。它的使用類似於Array,但是與Array又有不同:Buffer在定義的時候必須明確知道其長度,但是Array的長度是可以動態變化的。定義Buffer有三種方式:
1. var buf = new Buffer(3);//指定buffer占用3個字節
2. var buf = new Buffer("hello","utf-8");//指定了字符串,以及字符串的編碼類型為utf-8,占用5個字節
3. var buf = new Buffer([256,255,2]);//定義了buffer的每個字節所存儲的數字,最大為255,超過255取模
為什么介紹Buffer呢?因為本文所介紹的流就是以字節為單位傳輸的,而Buffer存儲的就是字節。
Node中用fs模塊的createReadStream和createWriteStream分別創建可讀流和可寫流。先介紹第一個接口。
一. createReadStream
這個方法繼承了events類,因而也可以發射和監聽相關事件。其用法如下:
var rs = fs.createReadStream(filePath , {options});
rs.on("data",function(data){
console.log(data);
});
rs.on("end",function(data){
console.log("resource file is completely read");
});
rs.on("error",function(err){
console.log("something is wrong during processing");
})
options是一組key-value值,常用的設置如下:
flags: 對文件進行何種操作,默認為'r',讀文件
encoding:指定編碼,默認為null,如果不設置具體的編碼格式,讀出的數據就是Buffer類型;也可以使用rs.setEncoding("utf-8")指定編碼格式
start:從start開始讀取文件
end:讀取文件到end為止(包括end)
highWaterMark:最高水位線,內部緩沖區最多能容納的字節數,如果超過這個大小,就停止讀取資源文件,默認值是64KB
前面幾個設置就遵從字面意思,比較好理解。重點講講highWaterMark。
比如有一個100KB的文件,設置highWaterMark為10KB,那么系統會先從資源文件中讀取出10KB的數據,再觸發data事件;然后再讀取10KB的數據,觸發data事件,不斷執行,直到讀出了所有的數據,觸發end事件。highWaterMark不能設置的過小,過小就會頻繁的操作文件,影響性能。
可讀流還有兩個重要的方法:pause和resume,分別可以禁止發射data事件以及激活發射data事件。這兩個方法稍后再講解。
二.createWriteStream
和createReadStream一樣,它也是events的一個子類。它的drain事件在緩沖區數據寫入完畢后被觸發。
其用法舉例:
var ws = fs.createWriteStream(filePath, {options});
其常用的options值為:
flags:對文件進行何種操作,默認為“w",代表寫文件;如果是"a",代表每次寫入的時候,不清空文件中的原有數據,而是直接在原有數據的末尾添加新數據
encoding:指定寫入的編碼格式,默認為null
start:文件開始寫入的位置
highWaterMark:緩存區能夠容納的最大字節數,默認為16KB,如果超過這個值,write方法就會返回false
可寫流的highWaterMark也代表了緩沖區的容量,如果緩沖區已經裝滿了,繼續寫入數據就會失敗。只有等緩沖區里的內容被寫入文件后,才可以重新調用write方法寫入下一個highWaterMark大小的數據(data chunk)。
可寫流也有兩個重要的方法:write和end,write定義了寫入的內容,end可以將還未寫入的內容強行寫入文件,並且關閉目標文件(不能繼續寫入了)。
基於以上分析,我們來實現一個文件拷貝的例子:
var fs=require("fs"); var rs = fs.createReadStream("./Koala.jpg");//默認64KB var ws = fs.createWriteStream("./Copy.jpg");//默認16KB,寫入速度小於讀取速度 rs.on("data",function(data){ var flag = ws.write(data); if (!flag){ //緩沖區已滿 rs.pause();//停止觸發data事件 } }); ws.on("drain",function(){ rs.resume();//如果當前的緩沖區寫入完畢,就重新觸發data事件 }); rs.on("end",function(){ ws.end();//將剩下的數據全部寫入,並且關閉寫入的文件 })
這個例子綜合運用了可讀流和可寫流的事件和重要方法。由於讀流比寫入流的速度快,所以要控制可讀流的數據流動。通過控制data事件的觸發時機,就解決讀寫速度不匹配的問題。