目錄編程
Node.js文件系統、路徑的操做函數 windows
2、寫文件 緩存
3、以追加方式寫文件 服務器
4、打開文件 閉包
6、寫文件,將緩衝區內數據寫入使用fs.open打開的文件 異步
7、刷新緩存區; ide
將Node.js的文件系統、文件流及路徑操做API詳細的學習了一下,代碼都是測試過的,也許很簡單,但爲了打好基礎,仍是要有點一絲不苟的精神,從中我也更深刻理解異步回調事件機制,但願對你有用……
//公共引用
1. var fs = require('fs'),
2. path = require('path');
//readFile(filename,[options],callback);
/**
* filename, 必選參數,文件名
* [options],可選參數,可指定flag(文件操做選項,如r+ 讀寫;w+ 讀寫,文件不存在則建立)及encoding屬性
* callback 讀取文件後的回調函數,參數默認第一個err,第二個data 數據
*/
1. fs.readFile(__dirname + '/test.txt', {flag: 'r+', encoding: 'utf8'}, function (err, data) {
2. if(err) {
3. console.error(err);
4. return;
5. }
6. console.log(data);
7. });
// fs.writeFile(filename,data,[options],callback);
var w_data = '這是一段經過fs.writeFile函數寫入的內容;\r\n';
var w_data = new Buffer(w_data);
/**
* filename, 必選參數,文件名
* data, 寫入的數據,能夠字符或一個Buffer對象
* [options],flag,mode(權限),encoding
* callback 讀取文件後的回調函數,參數默認第一個err,第二個data 數據
*/
1. fs.writeFile(__dirname + '/test.txt', w_data, {flag: 'a'}, function (err) {
2. if(err) {
3. console.error(err);
4. } else {
5. console.log('寫入成功');
6. }
7. });
// fs.appendFile(filename,data,[options],callback);
1. fs.appendFile(__dirname + '/test.txt', '使用fs.appendFile追加文件內容', function () {
2. console.log('追加內容完成');
3. });
// fs.open(filename, flags, [mode], callback);
/**
* filename, 必選參數,文件名
* flags, 操做標識,如"r",讀方式打開
* [mode],權限,如777,表示任何用戶讀寫可執行
* callback 打開文件後回調函數,參數默認第一個err,第二個fd爲一個整數,表示打開文件返回的文件描述符,window中又稱文件句柄
*/
1. fs.open(__dirname + '/test.txt', 'r', '0666', function (err, fd) {
2. console.log(fd);
3. });
// fs.read(fd, buffer, offset, length, position, callback);
/**
* fd, 使用fs.open打開成功後返回的文件描述符
* buffer, 一個Buffer對象,v8引擎分配的一段內存
* offset, 整數,向緩存區中寫入時的初始位置,以字節爲單位
* length, 整數,讀取文件的長度
* position, 整數,讀取文件初始位置;文件大小以字節爲單位
* callback(err, bytesRead, buffer), 讀取執行完成後回調函數,bytesRead實際讀取字節數,被讀取的緩存區對象
*/
1. fs.open(__dirname + '/test.txt', 'r', function (err, fd) {
2. if(err) {
3. console.error(err);
4. return;
5. } else {
6. var buffer = new Buffer(255);
7. console.log(buffer.length);
8. //每個漢字utf8編碼是3個字節,英文是1個字節
9. fs.read(fd, buffer, 0, 9, 3, function (err, bytesRead, buffer) {
10. if(err) {
11. throw err;
12. } else {
13. console.log(bytesRead);
14. console.log(buffer.slice(0, bytesRead).toString());
15. //讀取完後,再使用fd讀取時,基點是基於上次讀取位置計算;
16. fs.read(fd, buffer, 0, 9, null, function (err, bytesRead, buffer) {
17. console.log(bytesRead);
18. console.log(buffer.slice(0, bytesRead).toString());
19. });
20. }
21. });
22. }
23. });
//fs.write(fd, buffer, offset, length, position, callback);
/**
* fd, 使用fs.open打開成功後返回的文件描述符
* buffer, 一個Buffer對象,v8引擎分配的一段內存
* offset, 整數,從緩存區中讀取時的初始位置,以字節爲單位
* length, 整數,從緩存區中讀取數據的字節數
* position, 整數,寫入文件初始位置;
* callback(err, written, buffer), 寫入操做執行完成後回調函數,written實際寫入字節數,buffer被讀取的緩存區對象
*/
1. fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
2. if(err) {
3. console.error(err);
4. return;
5. } else {
6. var buffer = new Buffer('寫入文件數據內容');
7. //寫入'入文件'三個字
8. fs.write(fd, buffer, 3, 9, 12, function (err, written, buffer) {
9. if(err) {
10. console.log('寫入文件失敗');
11. console.error(err);
12. return;
13. } else {
14. console.log(buffer.toString());
15. fs.write(fd, buffer, 12, 9, null, function (err, written, buffer) {
16. console.log(buffer.toString());
17. })
18. }
19. });
20. }
21. });
// 使用fs.write寫入文件時,操做系統是將數據讀到內存,再把數據寫入到文件中,當數據讀完時並不表明數據已經寫完,由於有一部分還可能在內在緩衝區內。
// 所以可使用fs.fsync方法將內存中數據寫入文件;--刷新內存緩衝區;
//fs.fsync(fd, [callback])
/**
* fd, 使用fs.open打開成功後返回的文件描述符
* [callback(err, written, buffer)], 寫入操做執行完成後回調函數,written實際寫入字節數,buffer被讀取的緩存區對象
*/
1. fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
2. if(err)
3. throw err;
4. var buffer = new Buffer('我愛nodejs編程');
5.
6. fs.write(fd, buffer, 0, 9, 0, function (err, written, buffer) {
7. console.log(written.toString());
8. fs.write(fd, buffer, 9, buffer.length - 9, null, function (err, written) {
9. console.log(written.toString());
10. fs.fsync(fd);
11. fs.close(fd);
12. })
13. });
14. });
//使用fs.mkdir建立目錄
//fs.mkdir(path, [mode], callback);
/**
* path, 被建立目錄的完整路徑及目錄名;
* [mode], 目錄權限,默認0777
* [callback(err)], 建立完目錄回調函數,err錯誤對象
*/
1. fs.mkdir(__dirname + '/fsDir', function (err) {
2. if(err)
3. throw err;
4.
5. console.log('建立目錄成功')
6. });
//使用fs.readdir讀取目錄,重點其回調函數中files對象
//fs.readdir(path, callback);
/**
* path, 要讀取目錄的完整路徑及目錄名;
* [callback(err, files)], 讀完目錄回調函數;err錯誤對象,files數組,存放讀取到的目錄中的全部文件名
*/
1. fs.readdir(__dirname + '/fsDir/', function (err, files) {
2. if(err) {
3. console.error(err);
4. return;
5. } else {
6. files.forEach(function (file) {
7. var filePath = path.normalize(__dirname + '/fsDir/' + file);
8. fs.stat(filePath, function (err, stat) {
9. if(stat.isFile()) {
10. console.log(filePath + ' is: ' + 'file');
11. }
12. if(stat.isDirectory()) {
13. console.log(filePath + ' is: ' + 'dir');
14. }
15. });
16. });
17. for (var i = 0; i < files.length; i++) {
18. //使用閉包沒法保證讀取文件的順序與數組中保存的致
19. (function () {
20. var filePath = path.normalize(__dirname + '/fsDir/' + files[i]);
21. fs.stat(filePath, function (err, stat) {
22. if(stat.isFile()) {
23. console.log(filePath + ' is: ' + 'file');
24. }
25. if(stat.isDirectory()) {
26. console.log(filePath + ' is: ' + 'dir');
27. }
28. });
29. })();
30. }
31. }
32. });
//fs.stat(path, callback);
//fs.lstat(path, callback); //查看符號連接文件
/**
* path, 要查看目錄/文件的完整路徑及名;
* [callback(err, stats)], 操做完成回調函數;err錯誤對象,stat fs.Stat一個對象實例,提供如:isFile, isDirectory,isBlockDevice等方法及size,ctime,mtime等屬性
*/
//實例,查看fs.readdir
//fs.exists(path, callback);
/**
* path, 要查看目錄/文件的完整路徑及名;
* [callback(exists)], 操做完成回調函數;exists true存在,false表示不存在
*/
1. fs.exists(__dirname + '/te', function (exists) {
2. var retTxt = exists ? retTxt = '文件存在' : '文件不存在';
3. console.log(retTxt);
4. });
//fs.utimes(path, atime, mtime, callback);
/**
* path, 要查看目錄/文件的完整路徑及名;
* atime, 新的訪問時間
* ctime, 新的修改時間
* [callback(err)], 操做完成回調函數;err操做失敗對象
*/
1. fs.utimes(__dirname + '/test.txt', new Date(), new Date(), function (err) {
2. if(err) {
3. console.error(err);
4. return;
5. }
6. fs.stat(__dirname + '/test.txt', function (err, stat) {
7. console.log('訪問時間: ' + stat.atime.toString() + '; \n修改時間:' + stat.mtime);
8. console.log(stat.mode);
9. })
10. });
//fs.utimes(path, mode, callback);
/**
* path, 要查看目錄/文件的完整路徑及名;
* mode, 指定權限,如:0666 8進制,權限:全部用戶可讀、寫,
* [callback(err)], 操做完成回調函數;err操做失敗對象
*/
1. fs.chmod(__dirname + '/fsDir', 0666, function (err) {
2. if(err) {
3. console.error(err);
4. return;
5. }
6. console.log('修改權限成功')
7. });
//fs.rename(oldPath, newPath, callback);
/**
* oldPath, 原目錄/文件的完整路徑及名;
* newPath, 新目錄/文件的完整路徑及名;若是新路徑與原路徑相同,而只文件名不一樣,則是重命名
* [callback(err)], 操做完成回調函數;err操做失敗對象
*/
1. fs.rename(__dirname + '/test', __dirname + '/fsDir', function (err) {
2. if(err) {
3. console.error(err);
4. return;
5. }
6. console.log('重命名成功')
7. });
//fs.rmdir(path, callback);
/**
* path, 目錄的完整路徑及目錄名;
* [callback(err)], 操做完成回調函數;err操做失敗對象
*/
1. fs.rmdir(__dirname + '/test', function (err) {
2. fs.mkdir(__dirname + '/test', 0666, function (err) {
3. console.log('建立test目錄');
4. });
5.
6. if(err) {
7. console.log('刪除空目錄失敗,可能緣由:1、目錄不存在,2、目錄不爲空')
8. console.error(err);
9. return;
10. }
11.
12. console.log('刪除空目錄成功!');
13. });
//對文件進行監視,而且在監視到文件被修改時執行處理
//fs.watchFile(filename, [options], listener);
/**
* filename, 完整路徑及文件名;
* [options], persistent true表示持續監視,不退出程序;interval 單位毫秒,表示每隔多少毫秒監視一次文件
* listener, 文件發生變化時回調,有兩個參數:curr爲一個fs.Stat對象,被修改後文件,prev,一個fs.Stat對象,表示修改前對象
*/
1. fs.watchFile(__dirname + '/test.txt', {interval: 20}, function (curr, prev) {
2. if(Date.parse(prev.ctime) == 0) {
3. console.log('文件被建立!');
4. } else if(Date.parse(curr.ctime) == 0) {
5. console.log('文件被刪除!')
6. } else if(Date.parse(curr.mtime) != Date.parse(prev.mtime)) {
7. console.log('文件有修改');
8. }
9. });
10.
11. fs.watchFile(__dirname + '/test.txt', function (curr, prev) {
12. console.log('這是第二個watch,監視到文件有修改');
13. });
//取消對文件進行監視
//fs.unwatchFile(filename, [listener]);
/**
* filename, 完整路徑及文件名;
* [listener], 要取消的監聽器事件,若是不指定,則取消全部監聽處理事件
*/
1. var listener = function (curr, prev) {
2. console.log('我是監視函數')
3. }
4. fs.unwatchFile(__dirname + '/test.txt', listener);
16、監視文件或目錄
// 對文件或目錄進行監視,而且在監視到修改時執行處理;
// fs.watch返回一個fs.FSWatcher對象,擁有一個close方法,用於中止watch操做;
// 當fs.watch有文件變化時,會觸發fs.FSWatcher對象的change(err, filename)事件,err錯誤對象,filename發生變化的文件名
// fs.watch(filename, [options], [listener]);
/**
* filename, 完整路徑及文件名或目錄名;
* [listener(event, filename], 監聽器事件,有兩個參數:event 爲rename表示指定的文件或目錄中有重命名、刪除或移動操做或change表示有修改,filename表示發生變化的文件路徑
*/
1. var fsWatcher = fs.watch(__dirname + '/test', function (event, filename) {
2. //console.log(event)
3. });
4.
5. //console.log(fsWatcher instanceof FSWatcher);
6.
7. fsWatcher.on('change', function (event, filename) {
8. console.log(filename + ' 發生變化')
9. });
10.
11. //30秒後關閉監視
12. setTimeout(function () {
13. console.log('關閉')
14. fsWatcher.close(function (err) {
15. if(err) {
16. console.error(err)
17. }
18. console.log('關閉watch')
19. });
20. }, 30000);
/*
* 流,在應用程序中表示一組有序的、有起點有終點的字節數據的傳輸手段;
* Node.js中實現了stream.Readable/stream.Writeable接口的對象進行流數據讀寫;以上接口都繼承自EventEmitter類,所以在讀/寫流不一樣狀態時,觸發不一樣事件;
* 關於流讀取:Node.js不斷將文件一小塊內容讀入緩衝區,再從緩衝區中讀取內容;
* 關於流寫入:Node.js不斷將流數據寫入內在緩衝區,待緩衝區滿後再將緩衝區寫入到文件中;重複上面操做直到要寫入內容寫寫完;
* readFile、read、writeFile、write都是將整個文件放入內存而再操做,而則是文件一部分數據一部分數據操做;
*
* -----------------------流讀取-------------------------------------
* 讀取數據對象:
* fs.ReadStream 讀取文件
* http.IncomingMessage 客戶端請求或服務器端響應
* net.Socket Socket端口對象
* child.stdout 子進程標準輸出
* child.stdin 子進程標準入
* process.stdin 用於建立進程標準輸入流
* Gzip、Deflate、DeflateRaw 數據壓縮
*
* 觸發事件:
* readable 數據可讀時
* data 數據讀取後
* end 數據讀取完成時
* error 數據讀取錯誤時
* close 關閉流對象時
*
* 讀取數據的對象操做方法:
* read 讀取數據方法
* setEncoding 設置讀取數據的編碼
* pause 通知對象衆目中止觸發data事件
* resume 通知對象恢復觸發data事件
* pipe 設置數據通道,將讀入流數據接入寫入流;
* unpipe 取消通道
* unshift 當流數據綁定一個解析器時,此方法取消解析器
*
* ------------------------流寫入-------------------------------------
* 寫數據對象:
* fs.WriteStream 寫入文件對象
* http.clientRequest 寫入HTTP客戶端請求數據
* http.ServerResponse 寫入HTTP服務器端響應數據
* net.Socket 讀寫TCP流或UNIX流,須要connection事件傳遞給用戶
* child.stdout 子進程標準輸出
* child.stdin 子進程標準入
* Gzip、Deflate、DeflateRaw 數據壓縮
*
* 寫入數據觸發事件:
* drain 當write方法返回false時,表示緩存區中已經輸出到目標對象中,能夠繼續寫入數據到緩存區
* finish 當end方法調用,所有數據寫入完成
* pipe 當用於讀取數據的對象的pipe方法被調用時
* unpipe 當unpipe方法被調用
* error 當發生錯誤
*
* 寫入數據方法:
* write 用於寫入數據
* end 結束寫入,以後再寫入會報錯;
*/
//fs.createReadStream(path, [options])
/**
* path 文件路徑
* [options] flags:指定文件操做,默認'r',讀操做;encoding,指定讀取流編碼;autoClose, 是否讀取完成後自動關閉,默認true;start指定文件開始讀取位置;end指定文件開始讀結束位置
*/
1. var rs = fs.createReadStream(__dirname + '/test.txt', {start: 0, end: 2});
2.
3. //open是ReadStream對象中表示文件打開時事件,
4. rs.on('open', function (fd) {
5. console.log('開始讀取文件');
6. });
7.
8. rs.on('data', function (data) {
9. console.log(data.toString());
10. });
11.
12. rs.on('end', function () {
13. console.log('讀取文件結束')
14. });
15.
16. rs.on('close', function () {
17. console.log('文件關閉');
18. });
19.
20. rs.on('error', function (err) {
21. console.error(err);
22. });
23.
24. //暫停和回覆文件讀取;
25.
26. rs.on('open', function () {
27. console.log('開始讀取文件');
28. });
29.
30. rs.pause();
31.
32. rs.on('data', function (data) {
33. console.log(data.toString());
34. });
35.
36. setTimeout(function () {
37. rs.resume();
38. }, 2000);
//fs.createWriteStream(path, [options])
/**
* path 文件路徑
* [options] flags:指定文件操做,默認'w',;encoding,指定讀取流編碼;start指定寫入文件的位置
*/
/* ws.write(chunk, [encoding], [callback]);
* chunk, 能夠爲Buffer對象或一個字符串,要寫入的數據
* [encoding], 編碼
* [callback], 寫入後回調
*/
/* ws.end([chunk], [encoding], [callback]);
* [chunk], 要寫入的數據
* [encoding], 編碼
* [callback], 寫入後回調
*/
1. var ws = fs.createWriteStream(__dirname + '/test.txt', {start: 0});
2.
3. var buffer = new Buffer('我也喜歡你');
4.
5.
6. ws.write(buffer, 'utf8', function (err, buffer) {
7. console.log(arguments);
8. console.log('寫入完成,回調函數沒有參數')
9. });
10.
11. //最後再寫入的內容
12. ws.end('再見');
13.
14. //使用流完成複製文件操做
15. var rs = fs.createReadStream(__dirname + '/test.txt')
16. var ws = fs.createWriteStream(__dirname + '/test/test.txt');
17. rs.on('data', function (data) {
18. ws.write(data)
19. });
20.
21. ws.on('open', function (fd) {
22. console.log('要寫入的數據文件已經打開,文件描述符是: ' + fd);
23. });
24.
25. rs.on('end', function () {
26. console.log('文件讀取完成');
27.
28. ws.end('完成', function () {
29. console.log('文件所有寫入完成')
30. });
31. });
//關於WriteStream對象的write方法返回一個布爾類型,當緩存區中數據所有寫滿時,返回false;
//表示緩存區寫滿,並將當即輸出到目標對象中
//第一個例子
1. var ws = fs.createWriteStream(__dirname + '/test/test.txt');
2. for (var i = 0; i < 10000; i++) {
3. var w_flag = ws.write(i.toString());
4. //當緩存區寫滿時,輸出false
5. console.log(w_flag);
6. }
//第二個例子
1. var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
2. var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
3. rs.on('data', function (data) {
4. var flag = ws.write(data);
5. console.log(flag);
6. });
7.
8. //系統緩存區數據已經所有輸出觸發drain事件
9. ws.on('drain', function () {
10. console.log('系統緩存區數據已經所有輸出。')
11. });
//rs.pipe(destination, [options]);
/**
* destination 必須一個可寫入流數據對象
* [opations] end 默認爲true,表示讀取完成當即關閉文件;
*/
1. var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
2. var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
3. rs.pipe(ws);
4.
5. rs.on('data', function (data) {
6. console.log('數據可讀')
7. });
8.
9. rs.on('end', function () {
10. console.log('文件讀取完成');
11. //ws.end('再見')
12. });
//---------------------路徑操做-------------------------
//對window系統,目錄分隔爲'\', 對於UNIX系統,分隔符爲'/',針對'..'返回上一級;/與\\都被統一轉換
//path.normalize(p);
1. var myPath = path.normalize(__dirname + '/test/a//b//../c/utilyou.mp3');
2. console.log(myPath); //windows: E:\workspace\NodeJS\app\fs\test\a\c\utilyou.mp3
//path.join([path1],[path2]..[pathn]);
/**
* [path1] 路徑或表示目錄的字符,
*/
1. var path1 = 'path1',
2. path2 = 'path2//pp\\',
3. path3 = '../path3'
4.
5. var myPath = path.join(path1, path2, path3);
6. console.log(myPath); //path1\path2\path3
//path.resolve(path1, [path2]..[pathn]);
//以應用程序爲起點,根據參數字符串解析出一個絕對路徑
/**
* path 必須至少一個路徑字符串值
* [pathn] 可選路徑字符串
*/
1. var myPath = path.resolve('path1', 'path2', 'a/b\\c/');
2. console.log(myPath);//E:\workspace\NodeJS\path1\path2\a\b\c
//path.relative(from, to);
//獲取兩路徑之間的相對關係
/**
* from 當前路徑,而且方法返回值是基於from指定到to的相對路徑
* to 到哪路徑,
*/
1. var from = 'c:\\from\\a\\',
2. to = 'c:/test/b';
3. var _path = path.relative(from, to);
4. console.log(_path); //..\..\test\b; 表示從from到to的相對路徑
// 獲取路徑中目錄名
1. var myPath = path.dirname(__dirname + '/test/util you.mp3');
2. console.log(myPath);
// 獲取路徑中文件名,後綴是可選的,若是加,請使用'.ext'方式來匹配,則返回值中不包括後綴名;
1. var myPath = path.basename(__dirname + '/test/util you.mp3', '.mp3');
2. console.log(myPath);
獲取路徑中的擴展名,若是沒有'.',則返回空
返回操做系統中文件分隔符; window是'\\', Unix是'/'
返回操做系統中目錄分隔符,如window是';', Unix中是':'
《Node.js權威指南》之在Node.js中操做文件系統