文件系統是一種用於向用戶提供底層數據訪問的機制,同時也是一套實現了數據的存儲、分級組織、訪問和獲取等操作的抽象數據類型。
Node.js 中的fs模塊就是對文件系統的封裝,整合了一套標準 POSIX 文件 I/O 操作的集合,包括文件的讀寫、刪除、遍歷、重命名等操作。
fs 模塊中的所有方法都提供了三種形式:回調、同步和 Promise ,其中 Promise 是在 Node.js 的版本 10 中引入的。
本系列所有的示例源碼都已上傳至Github,點擊此處獲取。
一、三種形式
在回調形式的方法中,最後一個參數是其回調函數,會異步地調用,其中回調函數的第一個參數始終爲異常預留,不過有個例外是 exists() 方法。
回調形式不容易書寫,很容易就會形成回調地獄。
雖然同步形式的方法比較容易書寫,但是在執行時會阻止 Node.js 事件循環和阻塞 JavaScript 執行,直到操作完成。
Promise 形式的方法會使用底層的 Node.js 線程池,在事件循環線程之外異步地執行文件系統操作。對同一文件執行多個併發修改時必須小心,有可能會損壞數據。
以讀取文件爲例,三種形式的寫法如下所示,若不指定編碼,那麼輸出的將是 Buffer 實例。
const fs = require('fs'); // 回調 fs.readFile('./data.txt', 'utf8', (err, data) => { if (err) throw err; console.log(data); // strick }); // 同步 const data = fs.readFileSync('./data.txt', 'utf8'); console.log(data); // strick // Promise const { promises } = fs; async function readFilePromise() { const data = await promises.readFile('./data.txt', 'utf8'); console.log(data); // strick } readFilePromise();
二、基礎使用
1)判斷文件是否存在
exists() 方法可用於判斷文件是否存在,但在上一小節中曾提到,它已被棄用。
這是因爲此回調的參數與其他回調不一致。通常,Node.js 回調的第一個參數是 err 參數,然後跟可選的其他參數,但 fs.exists() 回調只有一個布爾參數,如下所示。
fs.exists('./data.txt', isExist => {
console.log(isExist);
});
再則是因爲 exists() 方法的功能用 access() 方法也能實現,其內部源碼如下所示,其實也是調用了 access() 方法。
function exists(path, callback) { maybeCallback(callback); // 構造回調函數 function suppressedCallback(err) { callback(err ? false : true); } try { fs.access(path, F_OK, suppressedCallback); } catch { return callback(false); } }
其中 F_OK 是 fs 模塊中的一個常量,表示文件是否存在,使用方法如下所示,R_OK 表示是否可讀,W_OK 表示是否可寫。
const { constants } = require('fs');
const { F_OK, R_OK, W_OK } = constants;
注意,在調用 fs.open()、fs.readFile() 或 fs.writeFile() 之前,不能使用 fs.access() 檢查文件是否存在。
因爲這樣做會引入競爭條件,其他進程可能會在兩次調用之間修改文件狀態,造成非預期的結果。
遇到這種場景,推薦的做法是直接打開、讀取或寫入文件,當文件不可用時再做處理。
另一種判斷文件是否存在的方法是調用 stat(),讀取文件屬性。
它有兩個方法 isDirectory() 和 isFile() 可分別判斷是否是目錄和是否是文件,如下所示。
fs.stat('./data.txt', (err, stats) => {
console.log(stats.isDirectory());
console.log(stats.isFile());
});
同樣要注意的是,它也不能在調用 fs.open()、fs.readFile() 或 fs.writeFile() 之前,檢查文件是否存在。
2)方法
下面羅列的是 fs 模塊的一些方法。
- fs.open():打開文件,可設置文件模式。
- fs.close():關閉文件描述符。
- fs.createReadStream():創建可讀的文件流。
- fs.createWriteStream():創建可寫的文件流。
- fs.readFile():讀取文件的內容,相關方法:fs.read()。
- fs.writeFile():寫入文件,相關方法:fs.write()。
- fs.link():新建指向文件的硬鏈接。
- fs.unlink():刪除文件或符號鏈接。
- fs.mkdir():新建文件夾。
- fs.rmdir():刪除文件夾。
- fs.readdir():讀取目錄的內容。
- fs.stat():讀取文件屬性,相關方法:fs.fstat()、fs.lstat()。
- fs.access():檢查文件是否存在,以及 Node.js 是否有權限訪問。
- fs.rename():重命名文件或文件夾。
- fs.appendFile():追加數據到文件,如果文件不存在,則創建文件。
- fs.copyFile():拷貝文件,可覆蓋文件內容。
- fs.chmod():更改文件(通過傳入的文件名指定)的權限,相關方法:fs.lchmod()、fs.fchmod()。
- fs.chown():更改文件(通過傳入的文件名指定)的所有者和羣組,相關方法:fs.fchown()、fs.lchown()。
- fs.watchFile():開始監控文件的更改,相關方法:fs.watch()。
- fs.unwatchFile():停止監控文件的更改。
3)路徑
路徑處理並不是在 fs 模塊,而是在path模塊,它的方法包括。
- path.basename():讀取路徑的最後一部分。
- path.dirname():讀取路徑的目錄部分。
- path.extname():讀取路徑的文件擴展名。
- path.isAbsolute():判斷是否是絕對路徑。
- path.join():將多個部分合併成一個完整的路徑。
- path.normalize():當包含類似 .、.. 或 // 等相對的說明符時,就嘗試計算實際的路徑。
- path.parse():解析成路徑對象。
- path.relative():基於當前目錄,返回從第一個路徑到第二個路徑的相對路徑。
- path.resolve():將相對路徑計算成絕對路徑。
path.basename('../06/data.txt') // data.txt path.dirname('../06/data.txt'); // ../06 path.extname('../06/data.txt'); // .txt path.isAbsolute('../06/data.txt'); // false path.join('../', '06', 'data.txt'); // ../06/data.txt path.normalize('/../06/data.txt'); // /06/data.txt // { root: '', dir: '../06', base: 'data.txt', ext: '.txt', name: 'data' } path.parse('../06/data.txt'); path.relative('../', '../06/data.txt'); // 06/data.txt path.resolve('../06/data.txt'); // /Users/code/web/node/06/data.txt
參考資料: