Node.js 內建模組之 fs

語言: CN / TW / HK

開啟掘金成長之旅!這是我參與「掘金日新計劃 · 2 月更文挑戰」的第 3 天,點選檢視活動詳情

fs(File system,檔案系統) 是 node 的一個內建模組,可用於在多種作業系統中(Windows 、macOS 等)對檔案進行操作。查閱 node 的官方文件,可以看到 File system 下有非常多的 API:
image.png

它們有的用於對檔案進行讀寫操作,有的用於對資料夾操作等,且都提供同步非同步Promise 這 3 種操作方式。下面舉幾個常用的 API 為例,進行說明。

操作檔案

讀取檔案

同步讀取

fs.readFileSync() 用於同步 (Sync) 讀取檔案,直接傳入要讀取的檔案的路徑或檔案描述符即可: javascript // 程式碼塊 1.1.1 const fs = require('fs') // 同步讀取 const resSync = fs.readFileSync('./test.txt') console.log(resSync) console.log('看看會不會阻塞') 執行結果如下,第 6 行的列印是在第 5 行之後,說明同步操作會阻塞後續程式碼的執行:

image.png

resSync 的結果是 Buffer 物件,本質上是二進位制的內容,只是以十六進位制來展示。如果想看到文字內容,可以對結果使用 toString() 進行轉換:console.log(resSync.toString()),或者給 fs.readFileSync() 傳入第 2 個引數 —— 一個用於指定配置的可選物件,該物件有 2 個屬性:

  • encoding,預設值為 null ,也就是結果顯示為 buffer,想顯示文字可以改為 'utf-8' (或 'utf8');
  • flag,讀取檔案時預設值為 'r',更多 flag 相關資訊可檢視官方文件javascript // 程式碼塊 1.1.2 const resSync = fs.readFileSync('./test.txt', { encoding: 'utf-8', flag: 'r' }) console.log(resSync) console.log('看看會不會阻塞') 現在看到的 resSync 就為一個字串了:

image.png

非同步讀取 - Callback

fs.readFile() 用於非同步讀取檔案,其可以傳入 3 個引數,前兩個與 fs.readFileSync() 相同,第 3 個引數為一個回撥函式,當獲取到讀取結果時被呼叫: javascript // 程式碼塊 1.2.1 fs.readFile( './test.txt', { encoding: 'utf-8' }, (err, data) => { if (!err) console.log(data) } ) console.log('看看會不會阻塞') 執行結果如下,可以看到非同步讀取不會阻塞後續程式碼的執行:

image.png

非同步讀取 - Promise

fs.promises.readFile() 也是用於非同步讀取檔案,但可以避免在採用回撥函式的方式獲取結果時容易產生的回撥地獄: javascript // 程式碼塊 1.3.1 fs.promises .readFile('./test.txt', { encoding: 'utf-8' }) .then(res => { console.log(res) }) .catch(err => console.log(err))

寫入檔案

寫入檔案也有同步非同步的方法,下面以非同步 - callback 的 api fs.writeFile()為例。
其第 1 個引數可以是檔案的地址或檔案描述符,如果檔案不存在,則會進行建立;
第 2 個引數為要寫入的內容;
第 3 個引數為用於指定配置的可選物件,其中有之前介紹過的兩個屬性 encodingflag,只不過這裡 encoding 的預設值為 'utf8'flag 的預設值為 'w',即預設寫入的內容會覆蓋原有的內容,如果是想在原內容後追加寫入,則可以改為 'a' (append);
第 4 個引數是一個回撥函式,在寫入操作執行結束後呼叫,如果寫入發生錯誤,就會把錯誤資訊作為引數傳入: javascript fs.writeFile( './test.txt', 'Hello Juejin', { encoding: 'utf8', flag: 'w' }, err => { if (err) { console.log(err) return } console.log('寫入成功') } )

檔案描述符

前面說到,讀取檔案的這 3 種方法,傳入的第一個引數除了檔案的路徑外,還可以是檔案描述符(file descriptor)。在常見的作業系統中,核心對於每個程序都維護著一張當前開啟的檔案和資源的表格,而每個開啟的檔案都會被分配一個簡單的數字用於標識和跟蹤檔案,雖然不同的作業系統具體實現可能不同,但 node 幫我們處理了差異,為每個開啟的檔案分配了個數字型別的檔案描述符。

我們可以通過 fs.open() 開啟一個檔案來獲取檔案描述符: javascript // 程式碼塊 2.1.1 fs.open('./test.txt', (err, fd) => { if (!err) { console.log(fd) } }) 檢視型別可以得知其第 2 個引數,也就是回撥函式會被傳入兩個引數,第 1 個為錯誤資訊 err,第 2 個就是檔案描述符 fd

image.png

得到的結果為 3:

image.png

檢視檔案資訊

獲取到了檔案描述符,我們可以通過 fs.fstat() (其第 1 個引數只能是 fd)檢視檔案資訊: javascript fs.open('./test.txt', (err, fd) => { if (err) return fs.fstat(fd, (err, stats) => { if (err) return console.log(stats) fs.close(fd) // 關閉檔案 }) }) 列印結果如下:

image.png

操作資料夾

建立資料夾

fs 模組還能對資料夾進行操作,比如 fs.mkdir() 就可以建立資料夾,mkdir 可以看成是 make directory 的縮寫: javascript fs.mkdir('./test', err => console.log(err)) // 建立 test 資料夾

讀取資料夾

fs.readdir() 可以用於讀取資料夾,在回撥函式中會返回讀取到的資料夾中所包含的檔案或資料夾,比如 node 目錄下的結構如下圖所示:

image.png

執行如下程式碼對 node 目錄進行讀取: javascript // fs.js fs.readdir('../node', (err, files) => { if (!err) console.log(files) }) 列印結果為:[ 'fs.js', 'test', 'test.txt' ]。 如果想獲取更詳細的資訊,可以傳入配置物件,將 withFileTypes 設定為 truejavascript fs.readdir('../node', { withFileTypes: true }, (err, files) => { if (!err) console.log(files) }) 那麼列印的結果就會通過 Symbol(type) 的值來表明是檔案(值為 1),還是資料夾(值為 2),關於 Symbol 的介紹可參看 Symbol 詳解

image.png

現在還有個問題,就是 test 資料夾裡的 index.txt 檔案沒有被讀取到,如果想讀取 node 目錄下的所有檔案,可以通過遞迴讀取: javascript function readDirRecursive(path) { fs.readdir(path, { withFileTypes: true }, (err, files) => { if (err) return files.forEach(item => { if (item.isDirectory()) { readDirRecursive(`${path}/${item.name}`) } else { console.log(item.name) } }) }) } readDirRecursive('../node') 我們也需要將 withFileTypes 設定為 true,這樣返回的 files 陣列中的元素才是一個個物件,並且有 name 屬性和 isDirectory() 方法可以判斷是否為資料夾。

重新命名資料夾/檔案

fs.rename() 可以重新命名資料夾或檔案,第 1 個引數為舊名稱,第 2 個引數傳新名稱即可: javascript fs.rename('./test', './juejin', err => console.log(err)) 感謝.gif 點贊.png
開啟掘金成長之旅!這是我參與「掘金日新計劃 · 2 月更文挑戰」的第 3 天,點選檢視活動詳情