前言 node中有一組流api,它們可以像處理網路流一樣處理文件。流api用起來非常方便,本節學習介紹文件處理基礎和流的概念。 目錄 1. 處理文件路徑 處理文件路徑需要用到一個核心模塊(path),path模塊可以規範化、連接、解析路徑,還可以將絕對路徑轉換為相對路徑,提取路徑的組成以及判斷路徑是 ...
前言
node中有一組流api,它們可以像處理網路流一樣處理文件。流api用起來非常方便,本節學習介紹文件處理基礎和流的概念。
目錄
- 處理文件路徑
- fs核心模塊簡介
- 操作流
- 慢客戶端問題
1. 處理文件路徑
處理文件路徑需要用到一個核心模塊(path),path模塊可以規範化、連接、解析路徑,還可以將絕對路徑轉換為相對路徑,提取路徑的組成以及判斷路徑是否存在。
1.1. 規範化路徑
在存儲和使用路徑前最好能夠將其進行規範化,這裡使用path模塊的normalize()函數
1 //規範化路徑 2 var path=require('path'); 3 path.normalize('/pathstudy//path1') 4 // =>'/pathstudy/path1'
1.2. 連接路徑
通過path.join()函數可以連接多個路徑字元串,如下所示:
1 //連接路徑 2 var path=require('path'); 3 path.join('/pathstudy','path1','test'); 4 // => '/pathstudy/path1/test'
1.3. 解析路徑
使用path.resolve()函數將多個路徑解析為一個規範化的路徑
1 //解析路徑 2 var path=require('path'); 3 path.resolve('/foo/bar','./dd'); 4 // =>/foo/bar/dd
1.4. 提取路徑的組成部分
1 //提取路徑組成部分 2 var path=require('path'); 3 path.dirname('/foo/bar/dd/a1.txt');//獲取文件路徑的目錄 => /foo/bar/dd 4 path.basename('/foo/bar/dd/a1.txt');//獲取文件名 => a1.txt 5 path.basename('/foo/bar/dd/a1.txt','.txt');//獲取不包含文件類型的文件名 => a1 6 path.extname('/foo/bar/dd/a1.txt','.txt');//獲取文件擴展類型 =>.txt
1.5. 判斷路徑是否存在
使用path.exists()函數判斷路徑是否存在,0.8版本以後使用fs.exists()函數代替,只需將導入模塊替換為fs即可。
1 //判斷路徑是否存在 2 var path=require('path'); 3 path.exists('/foo/bar/dd',function(exists){ 4 console.log('是否存在:',exists); 5 })
2. fs模塊簡介
fs核心模塊內置查詢文件信息和打開/讀寫/關閉文件操作
查詢文件信息
使用fs.stat()函數查詢文件特征,例如大小、創建時間、許可權等
//查詢文件信息 var fs=require('fs'); fs.stat('/foo/bar/dd',function(stats){ console.log(stats);//列印出文件信息 stats.isFile();//判斷是否文件 stats.isDirectory();//判斷是否目錄 stats.isSocket();//判斷是否UNIX套接字 //... });
打開/讀寫/關閉文件
1 var fs=require('fs'); 2 //打開文件,‘r’表示模式類型 3 fs.open('/foo/bar/dd/a1.txt','r',function(err,fd){ 4 //獲取文件描述符 fd 5 }); 6 7 //讀取文件 8 fs.open('/foo/bar/dd/a1.txt','r',function(err,fd){ 9 if(err){ 10 throw err; 11 } 12 var readbuffer=new Buffer(1024),//設置緩衝區 13 bufferOffset=0, 14 filePosition=100,//從100位元組開始 15 bufferLength=readbuffer.length;//讀取隨後的1024位元組數據; 16 fs.read(fd,readbuffer,bufferOffset,bufferLength,filePosition, 17 function (err, readBytes){ 18 if(err){ 19 throw err; 20 } 21 if(readBytes>0){ 22 console.log(readbuffer.slice(0,readBytes));//獲取讀入緩衝區的位元組數 23 } 24 }); 25 }); 26 27 //寫入文件 28 fs.open('/foo/bar/dd/a1.txt','a',function(err,fd){ 29 if(err){ 30 throw err; 31 } 32 var writebuffer=new Buffer('準備寫入緩衝區的數據'),//設置緩衝區 33 bufferPosition=0,//寫入起始位置 34 filePosition=null,//從文件什麼位置開始寫 35 bufferLength=readbuffer.length;//長度 36 fs.write(fd,writebuffer,bufferPosition,bufferLength,filePosition, 37 function (err, write){ 38 if(err){ 39 throw err; 40 } 41 console.log('write bytes:',write);//獲取讀入緩衝區的位元組數 42 }); 43 }); 44 45 //關閉文件 46 fs.open('/foo/bar/dd/a1.txt','a',function(err,fd){ 47 if(err){ 48 throw err; 49 } 50 //write... 51 //close 52 fs.close(fd,function(){ 53 //... 54 }) 55 });View Code
文件模式類型:
r——數據流的位置從起始處打開文件讀取
r+——數據流的位置從起始處打開文件讀寫
w——如果文件存在則清零,不存在則創建文件並寫入數據,數據流的位置從文件起始處
w+——打開文件進行讀寫,如果文件存在則清零,不存在則創建文件並寫入數據,數據流的位置從文件起始處
a——打開文件寫入數據,如果文件存在則清零,不存在則創建文件,數據流的位置從文件結尾處,寫操作都將追加到文件後面
a+——打開文件讀寫數據,如果文件存在則清零,不存在則創建文件,數據流的位置從文件結尾處,寫操作都將追加到文件後面
需要註意,在讀寫文件操作回調函數執行之後,不要使用提供的緩衝區,不然可能會導致讀寫數據不完整。
3. 操作流
流是由幾個node對象實現的抽象概念,創建或讀取流的方式取決於使用流的類型,流的特性包含可讀流和可寫流,通過監聽data事件,在流每次提交數據時,都能夠得到通知。
3.1. 創建文件系統流
1 var fs=require('fs'); 2 //根據文件路徑創建一個可讀流 3 fs.createReadStream('/foo/bar/file'); 4 5 fs.open('/foo/bar/file','r',function(err,fd){ 6 fs.createReadStream(null,{fd:fd,encoding:'utf8'}); 7 fs.on('data',function(data){ 8 console.log('data:',data); 9 }); 10 }); 11 12 //創建可寫流 13 fs.createWriteStream('/foo/bar/file'); 14 15 fs.createWriteStream('/foo/bar/file',option); 16 //option表示第二個參數,預設值為{flags:'w',encoding:null,mode:0666}
可以指定createReadStream和createWriteStream函數的第二個參數,用來設置文件的起始結束位置、編碼格式等等,參數選項如下所示:
- encoding——data事件發送的編碼格式
- fd——如果已經有一個打開文件描述符則可以傳入該參數選項
- bufferSize——要被讀取的每個文件塊大小,單位是位元組,預設64kb
- start——設置文件中第一個被讀取的位元組位置,用來限制讀取數據範圍
- end——與start相反
- flags——表示用於打開文件的模式類型
- mode——指定要打開文件的許可權
4. 慢客戶端問題
當客戶端的網路連接速度較慢時,可寫流也就慢速,可讀流會快速產生data事件並監聽,數據被髮送到可寫流,導致node不得不緩存數據導致緩衝區被快速填滿。
一般情況可以通過暫停數據生產者來避免這個問題:
1 var http=require('http'); 2 var fs=require('fs'); 3 http.createServer(function(req,res){ 4 var rs= fs.createReadStream('/foo/bar/file'); 5 6 rs.on('data',function(data){ 7 if(!res.write(data)){ 8 rs.pause();//暫停可讀流 9 } 10 }); 11 12 rs.on('drain',function(){ 13 rs.resume();//恢復可讀流 14 }); 15 16 rs.on('end',function(){ 17 res.end();//結束操作 18 }); 19 20 }).listen('8080');View Code
擴展:使用pipe函數操作上述暫停/恢復操作
1 var http=require('http'); 2 var fs=require('fs'); 3 http.createServer(function(req,res){ 4 var rs= fs.createReadStream('/foo/bar/file'); 5 rs.pipe(res);//由傳輸源調用並接受目標可寫流作為參數 6 }).listen('8080'); 7 8 http.createServer(function(req,res){ 9 var rs= fs.createReadStream('/foo/bar/file'); 10 //預設情況下end()在可讀流結束時在可寫流上被調用,設置第二個參數end選項為false表示不讓pipe函數進行end操作 11 rs.pipe(res,{end:false}); 12 13 rs.on('end',function(){ 14 res.write('endend~~'); 15 res.end();//結束操作 16 }); 17 }).listen('8080');View Code
總結
對於流我表示比較懵逼,對概念理解的還不是很透徹。。