1. 程式人生 > 實用技巧 >node對檔案及資料夾的操作

node對檔案及資料夾的操作

//公共引用
var fs = require('fs'),
path = require('path');

1、讀取檔案readFile函式

//readFile(filename,[options],callback);

/**
 * filename, 必選引數,檔名
 * [options],可選引數,可指定flag(檔案操作選項,如r+ 讀寫;w+ 讀寫,檔案不存在則建立)及encoding屬性
 * callback 讀取檔案後的回撥函式,引數預設第一個err,第二個data 資料
 */

fs.readFile(__dirname + '/test.txt', {flag: 'r+', encoding: 'utf8'}, function (err, data) {
    if(err) {
     console.error(err);
     return;
    }
    console.log(data);
});

  

2、寫檔案

// 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 資料
 */

fs.writeFile(__dirname + '/test.txt', w_data, {flag: 'a'}, function (err) {
   if(err) {
    console.error(err);
    } else {
       console.log('寫入成功');
    }
});

  

3、以追加方式寫檔案

// fs.appendFile(filename,data,[options],callback);

fs.appendFile(__dirname + '/test.txt', '使用fs.appendFile追加檔案內容', function () {
  console.log('追加內容完成');
});

  

4、開啟檔案

// fs.open(filename, flags, [mode], callback);

/**
 * filename, 必選引數,檔名
 * flags, 操作標識,如"r",讀方式開啟
 * [mode],許可權,如777,表示任何使用者讀寫可執行
 * callback 開啟檔案後回撥函式,引數預設第一個err,第二個fd為一個整數,表示開啟檔案返回的檔案描述符,window中又稱檔案控制代碼
 */

fs.open(__dirname + '/test.txt', 'r', '0666', function (err, fd) {
  console.log(fd);
});

  

5、讀檔案,讀取開啟的檔案內容到緩衝區中;

//fs.read(fd, buffer, offset, length, position, callback);
/**
 * fd, 使用fs.open開啟成功後返回的檔案描述符
 * buffer, 一個Buffer物件,v8引擎分配的一段記憶體
 * offset, 整數,向快取區中寫入時的初始位置,以位元組為單位
 * length, 整數,讀取檔案的長度
 * position, 整數,讀取檔案初始位置;檔案大小以位元組為單位
 * callback(err, bytesRead, buffer), 讀取執行完成後回撥函式,bytesRead實際讀取位元組數,被讀取的快取區物件
 */

fs.open(__dirname + '/test.txt', 'r', function (err, fd) {
  if(err) {
    console.error(err);
    return;
  } else {
    var buffer = new Buffer(255);
    console.log(buffer.length);
    //每一個漢字utf8編碼是3個位元組,英文是1個位元組
    fs.read(fd, buffer, 0, 9, 3, function (err, bytesRead, buffer) {
      if(err) {
        throw err;
      } else {
        console.log(bytesRead);
        console.log(buffer.slice(0, bytesRead).toString());
        //讀取完後,再使用fd讀取時,基點是基於上次讀取位置計算;
        fs.read(fd, buffer, 0, 9, null, function (err, bytesRead, buffer) {
          console.log(bytesRead);
          console.log(buffer.slice(0, bytesRead).toString());
        });
      }
    });
  }
});

  

6、寫檔案,將緩衝區內資料寫入使用fs.open開啟的檔案

//fs.write(fd, buffer, offset, length, position, callback);

/**
 * fd, 使用fs.open開啟成功後返回的檔案描述符
 * buffer, 一個Buffer物件,v8引擎分配的一段記憶體
 * offset, 整數,從快取區中讀取時的初始位置,以位元組為單位
 * length, 整數,從快取區中讀取資料的位元組數
 * position, 整數,寫入檔案初始位置;
 * callback(err, written, buffer), 寫入操作執行完成後回撥函式,written實際寫入位元組數,buffer被讀取的快取區物件
 */

fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
  if(err) {
    console.error(err);
    return;
  } else {
    var buffer = new Buffer('寫入檔案資料內容');
    //寫入'入檔案'三個字
    fs.write(fd, buffer, 3, 9, 12, function (err, written, buffer) {
      if(err) {
        console.log('寫入檔案失敗');
        console.error(err);
        return;
      } else {
        console.log(buffer.toString());
        //寫入'資料內'三個字
        fs.write(fd, buffer, 12, 9, null, function (err, written, buffer) {
          console.log(buffer.toString());
        })
      }
    });
  }
});

  

7、重新整理快取區;

// 使用fs.write寫入檔案時,作業系統是將資料讀到記憶體,再把資料寫入到檔案中,當資料讀完時並不代表資料已經寫完,因為有一部分還可能在內在緩衝區內。
// 因此可以使用fs.fsync方法將記憶體中資料寫入檔案;--重新整理記憶體緩衝區;

//fs.fsync(fd, [callback])
/**
 * fd, 使用fs.open開啟成功後返回的檔案描述符
 * [callback(err, written, buffer)], 寫入操作執行完成後回撥函式,written實際寫入位元組數,buffer被讀取的快取區物件
 */

fs.open(__dirname + '/test.txt', 'a', function (err, fd) {
  if(err)
    throw err;
  var buffer = new Buffer('我愛nodejs程式設計');
  fs.write(fd, buffer, 0, 9, 0, function (err, written, buffer) {
    console.log(written.toString());
    fs.write(fd, buffer, 9, buffer.length - 9, null, function (err, written) {
      console.log(written.toString());
      fs.fsync(fd);
      fs.close(fd);
    })
  });
});

  

8、建立目錄;

//使用fs.mkdir建立目錄
//fs.mkdir(path, [mode], callback);

/**
 * path, 被建立目錄的完整路徑及目錄名;
 * [mode], 目錄許可權,預設0777
 * [callback(err)], 建立完目錄回撥函式,err錯誤物件
 */

fs.mkdir(__dirname + '/fsDir', function (err) {
  if(err)
    throw err;
  console.log('建立目錄成功')
});

  

9、讀取目錄;

//使用fs.readdir讀取目錄,重點其回撥函式中files物件
//fs.readdir(path, callback);

/**
 * path, 要讀取目錄的完整路徑及目錄名;
 * [callback(err, files)], 讀完目錄回撥函式;err錯誤物件,files陣列,存放讀取到的目錄中的所有檔名
 */

fs.readdir(__dirname + '/fsDir/', function (err, files) {
  if(err) {
    console.error(err);
    return;
  } else {
    files.forEach(function (file) {
      var filePath = path.normalize(__dirname + '/fsDir/' + file);
      fs.stat(filePath, function (err, stat) {
        if(stat.isFile()) {
          console.log(filePath + ' is: ' + 'file');
        }
        if(stat.isDirectory()) {
          console.log(filePath + ' is: ' + 'dir');
        }
      });
    });
    for (var i = 0; i < files.length; i++) {
      //使用閉包無法保證讀取檔案的順序與陣列中儲存的致
      (function () {
        var filePath = path.normalize(__dirname + '/fsDir/' + files[i]);
        fs.stat(filePath, function (err, stat) {
          if(stat.isFile()) {
            console.log(filePath + ' is: ' + 'file');
          }
          if(stat.isDirectory()) {
            console.log(filePath + ' is: ' + 'dir');
          }
        });
      })();
    }
  }
});

  

10、檢視檔案與目錄的資訊;

//fs.stat(path, callback);
//fs.lstat(path, callback); //檢視符號連結檔案
/**
 * path, 要檢視目錄/檔案的完整路徑及名;
 * [callback(err, stats)], 操作完成回撥函式;err錯誤物件,stat fs.Stat一個物件例項,提供如:isFile, isDirectory,isBlockDevice等方法及size,ctime,mtime等屬性
 */

//例項,檢視fs.readdir

  

11、檢視檔案與目錄的是否存在

//fs.exists(path, callback);

/**
 * path, 要檢視目錄/檔案的完整路徑及名;
 * [callback(exists)], 操作完成回撥函式;exists true存在,false表示不存在
 */

fs.exists(__dirname + '/te', function (exists) {
  var retTxt = exists ? retTxt = '檔案存在' : '檔案不存在';
  console.log(retTxt);
});

  

12、修改檔案訪問時間與修改時間

//fs.utimes(path, atime, mtime, callback);

/**
 * path, 要檢視目錄/檔案的完整路徑及名;
 * atime, 新的訪問時間
 * ctime, 新的修改時間
 * [callback(err)], 操作完成回撥函式;err操作失敗物件
 */

fs.utimes(__dirname + '/test.txt', new Date(), new Date(), function (err) {
  if(err) {
    console.error(err);
    return;
  }
  fs.stat(__dirname + '/test.txt', function (err, stat) {
    console.log('訪問時間: ' + stat.atime.toString() + '; \n修改時間:' + stat.mtime);
    console.log(stat.mode);
  })
});

  

13、修改檔案或目錄的操作許可權

//fs.utimes(path, mode, callback);

/**
 * path, 要檢視目錄/檔案的完整路徑及名;
 * mode, 指定許可權,如:0666 8進位制,許可權:所有使用者可讀、寫,
 * [callback(err)], 操作完成回撥函式;err操作失敗物件
 */

fs.chmod(__dirname + '/fsDir', 0666, function (err) {
  if(err) {
    console.error(err);
    return;
  }
  console.log('修改許可權成功')
});

  

14、移動/重新命名檔案或目錄

//fs.rename(oldPath, newPath, callback);

/**
 * oldPath, 原目錄/檔案的完整路徑及名;
 * newPath, 新目錄/檔案的完整路徑及名;如果新路徑與原路徑相同,而只檔名不同,則是重新命名
 * [callback(err)], 操作完成回撥函式;err操作失敗物件
 */
fs.rename(__dirname + '/test', __dirname + '/fsDir', function (err) {
  if(err) {
    console.error(err);
    return;
  }
  console.log('重新命名成功')
});

  

15、刪除空目錄

//fs.rmdir(path, callback);

/**
 * path, 目錄的完整路徑及目錄名;
 * [callback(err)], 操作完成回撥函式;err操作失敗物件
 */

fs.rmdir(__dirname + '/test', function (err) {
  fs.mkdir(__dirname + '/test', 0666, function (err) {
    console.log('建立test目錄');
  });
  if(err) {
    console.log('刪除空目錄失敗,可能原因:1、目錄不存在,2、目錄不為空')
    console.error(err);
    return;
  }
  console.log('刪除空目錄成功!');
});

  

16、監視檔案

//對檔案進行監視,並且在監視到檔案被修改時執行處理
//fs.watchFile(filename, [options], listener);

/**
 * filename, 完整路徑及檔名;
 * [options], persistent true表示持續監視,不退出程式;interval 單位毫秒,表示每隔多少毫秒監視一次檔案
 * listener, 檔案發生變化時回撥,有兩個引數:curr為一個fs.Stat物件,被修改後檔案,prev,一個fs.Stat物件,表示修改前物件
 */

fs.watchFile(__dirname + '/test.txt', {interval: 20}, function (curr, prev) {
  if(Date.parse(prev.ctime) == 0) {
    console.log('檔案被建立!');
  } else if(Date.parse(curr.ctime) == 0) {
    console.log('檔案被刪除!')
  } else if(Date.parse(curr.mtime) != Date.parse(prev.mtime)) {
    console.log('檔案有修改');
  }
});
fs.watchFile(__dirname + '/test.txt', function (curr, prev) {
  console.log('這是第二個watch,監視到檔案有修改');
});

  

17、取消監視檔案

//取消對檔案進行監視
//fs.unwatchFile(filename, [listener]);

/**
 * filename, 完整路徑及檔名;
 * [listener], 要取消的監聽器事件,如果不指定,則取消所有監聽處理事件
 */

var listener = function (curr, prev) {
  console.log('我是監視函式')
}
fs.unwatchFile(__dirname + '/test.txt', listener);

  

18、監視檔案或目錄

// 對檔案或目錄進行監視,並且在監視到修改時執行處理;
// 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表示發生變化的檔案路徑
 */

var fsWatcher = fs.watch(__dirname + '/test', function (event, filename) {
  //console.log(event)
});

//console.log(fsWatcher instanceof FSWatcher);

fsWatcher.on('change', function (event, filename) {
  console.log(filename + ' 發生變化')
});

//30秒後關閉監視
setTimeout(function () {
  console.log('關閉')
  fsWatcher.close(function (err) {
    if(err) {
      console.error(err)
    }
    console.log('關閉watch')
  });
}, 30000);

  

19、檔案流

/*
 * 流,在應用程式中表示一組有序的、有起點有終點的位元組資料的傳輸手段;
 * 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              結束寫入,之後再寫入會報錯;
 */

  

20、建立讀取流

//fs.createReadStream(path, [options])
/**
 * path 檔案路徑
 * [options] flags:指定檔案操作,預設'r',讀操作;encoding,指定讀取流編碼;autoClose, 是否讀取完成後自動關閉,預設true;start指定檔案開始讀取位置;end指定檔案開始讀結束位置
 */

var rs = fs.createReadStream(__dirname + '/test.txt', {start: 0, end: 2});
  //open是ReadStream物件中表示檔案開啟時事件,
rs.on('open', function (fd) {
  console.log('開始讀取檔案');
});

rs.on('data', function (data) {
  console.log(data.toString());
});

rs.on('end', function () {
  console.log('讀取檔案結束')
});
rs.on('close', function () {
  console.log('檔案關閉');
});

rs.on('error', function (err) {
  console.error(err);
});

//暫停和回覆檔案讀取;
rs.on('open', function () {
  console.log('開始讀取檔案');
});

rs.pause();

rs.on('data', function (data) {
  console.log(data.toString());
});

setTimeout(function () {
  rs.resume();
}, 2000);

  

21、建立寫入流

//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],  寫入後回撥
 */

var ws = fs.createWriteStream(__dirname + '/test.txt', {start: 0});
var buffer = new Buffer('我也喜歡你');
ws.write(buffer, 'utf8', function (err, buffer) {
  console.log(arguments);
  console.log('寫入完成,回撥函式沒有引數')
});
//最後再寫入的內容
ws.end('再見');
//使用流完成複製檔案操作
var rs = fs.createReadStream(__dirname + '/test.txt')
var ws = fs.createWriteStream(__dirname + '/test/test.txt');

rs.on('data', function (data) {
  ws.write(data)
});

ws.on('open', function (fd) {
  console.log('要寫入的資料檔案已經開啟,檔案描述符是: ' + fd);
});

rs.on('end', function () {
  console.log('檔案讀取完成');
  ws.end('完成', function () {
    console.log('檔案全部寫入完成')
  });
});


//關於WriteStream物件的write方法返回一個布林型別,當快取區中資料全部寫滿時,返回false;
//表示快取區寫滿,並將立即輸出到目標物件中

//第一個例子
var ws = fs.createWriteStream(__dirname + '/test/test.txt');
for (var i = 0; i < 10000; i++) {
  var w_flag = ws.write(i.toString());
  //當快取區寫滿時,輸出false
  console.log(w_flag);
}


//第二個例子
var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
rs.on('data', function (data) {
  var flag = ws.write(data);
  console.log(flag);
});

//系統快取區資料已經全部輸出觸發drain事件
ws.on('drain', function () {
  console.log('系統快取區資料已經全部輸出。')
});

  

22、管道pipe實現流讀寫

//rs.pipe(destination, [options]);
/**
 * destination 必須一個可寫入流資料物件
 * [opations] end 預設為true,表示讀取完成立即關閉檔案;
 */

var rs = fs.createReadStream(__dirname + '/test/Until You.mp3');
var ws = fs.createWriteStream(__dirname + '/test/untiyou.mp3');
rs.pipe(ws);
rs.on('data', function (data) {
  console.log('資料可讀')
});
rs.on('end', function () {
  console.log('檔案讀取完成');
  //ws.end('再見')
});