>[success] # 內置模塊fs
1. fs是`File System`的縮寫,表示文件系統,文件讀取、寫入、復制、重命名、刪除、文件權限的設置等一系列的操作方法
2. 這些API大多數都提供三種操作方式
* **同步操作文件**:代碼會被阻塞,不會繼續執行;
* **異步回調函數操作文件**:代碼不會被阻塞,需要傳入回調函數,當獲取到結果時,回調函數被執行
* **異步Promise操作文件**:代碼不會被阻塞,通過 fs.promises 調用方法操作,會返回一個Promise,可以通過then、catch進行處理
3. 關于`fs.promises` 風格,在 Node.js 14.x 版本中,許多 `fs` 模塊的 API 都添加了 `fs.promises` 的支持,這意味著您可以使用類似 `fs.promises.readFile` 這樣的 Promise 風格的異步 API 來代替傳統的回調函數式異步 API。使用 `fs.promises` 可以方便地使用 async/await 語法糖來處理異步操作,從而使異步編程更加簡單易懂。 但是,您需要注意的是,并非所有的 `fs` 模塊的 API 都支持 `fs.promises`。例如,`fs.read` 和 `fs.write` 這兩個 API 就不支持 `fs.promises`,因此只能使用傳統的回調函數式異步 API 或者同步 API 來進行操作。 如果您需要使用同步 API,可以使用同名同參數的 API 來代替異步 API。例如,使用 `fs.readFileSync` 來代替 `fs.promises.readFile`,使用 `fs.mkdirSync` 來代替 `fs.promises.mkdir`。需要注意的是,同步 API 會阻塞程序的執行,因此需要謹慎使用,特別是在 IO 密集型的場景下。
4. 正常的同步api 和 通過async await 實現的同步`fs.promises` 有什么區別?舉個例子
~~~
const fs = require('fs')
async function readA() {
const a = await fs.promises.readFile('a.txt')
console.log(a)
}
const b = fs.readFileSync('a.txt')
readA()
console.log(b)
~~~
變量 b 是通過 `fs.readFileSync` 同步地讀取文件內容得到的,因此當執行到 `console.log(b)` 時,**文件內容已經被完全讀取并存儲在變量 b 中,可以直接輸出**。 相比之下,變量 a 是通過 `fs.promises.readFile` 異步地讀取文件內容得到的。在執行到 `readA()` 時,**雖然異步讀取文件的操作已經開始,但此時文件內容還沒有被完全讀取并存儲在變量 a 中**,使用了 `await `的異步操作本質上仍然是異步的,只不過是使用了 `Promise `和 `async/await` 語法糖來簡化異步編程的復雜度。因此,雖然使用了 `await`,變量 a 仍然是異步讀取文件得到的結果,和 b 的同步讀取結果有本質的區別。 具體來說,使用 `await `的異步操作會立即返回一個 `Promise `對象,在異步操作完成后,`Promise `對象的狀態會從 `pending `變為 `fulfilled `或 `rejected`,并將異步操作的結果傳遞給 `Promise `的 then 方法或者 `catch `方法。在 `await `后的表達式中,如果 `Promise `的狀態為 `fulfilled`,則會返回 `Promise `綁定的值;如果 `Promise `的狀態為 `rejected`,則會拋出 `Promise `綁定的錯誤。 因此,在這段代碼中,當執行到 `await fs.promises.readFile('a.txt')` 時,異步讀取文件操作已經開始,但由于此時文件內容還未被完全讀取,因此 `await `表達式會暫停代碼的執行,直到異步操作完成并返回 `Promise `對象后,再繼續執行下面的代碼因此**變量 a 是異步讀取文件的結果,和變量 b 的同步讀取結果有本質的區別。**
| API | 描述 | 同步/異步 | fs.promises 調用方法 |
| --- | --- | --- | --- |
| fs.readFile(path \[, options\], callback) | 異步讀取文件內容 | 異步 | fs.promises.readFile(path \[, options\]) |
| fs.readFileSync(path \[, options\]) | 同步讀取文件內容 | 同步 | fs.promises.readFile(path \[, options\]) |
| fs.writeFile(file, data \[, options\], callback) | 異步寫入文件內容 | 異步 | fs.promises.writeFile(file, data \[, options\]) |
| fs.writeFileSync(file, data \[, options\]) | 同步寫入文件內容 | 同步 | fs.promises.writeFile(file, data \[, options\]) |
| fs.mkdir(path \[, options\], callback) | 異步創建目錄 | 異步 | fs.promises.mkdir(path \[, options\]) |
| fs.mkdirSync(path \[, options\]) | 同步創建目錄 | 同步 | fs.promises.mkdir(path \[, options\]) |
| fs.readdir(path \[, options\], callback) | 異步讀取目錄下的所有文件和子目錄 | 異步 | fs.promises.readdir(path \[, options\]) |
| fs.readdirSync(path \[, options\]) | 同步讀取目錄下的所有文件和子目錄 | 同步 | fs.promises.readdir(path \[, options\]) |
| fs.stat(path, callback) | 異步獲取文件或目錄的狀態信息 | 異步 | fs.promises.stat(path) |
| fs.statSync(path) | 同步獲取文件或目錄的狀態信息 | 同步 | fs.promises.stat(path) |
| fs.rename(oldPath, newPath, callback) | 異步重命名文件 | 異步 | fs.promises.rename(oldPath, newPath) |
| fs.renameSync(oldPath, newPath) | 同步重命名文件 | 同步 | fs.promises.rename(oldPath, newPath) |
| fs.unlink(path, callback) | 異步刪除文件 | 異步 | fs.promises.unlink(path) |
| fs.unlinkSync(path) | 同步刪除文件 | 同步 | fs.promises.unlink(path) |
| fs.rmdir(path, callback) | 異步刪除目錄 | 異步 | fs.promises.rmdir(path) |
| fs.rmdirSync(path) | 同步刪除目錄 | 同步 | fs.promises.rmdir(path) |
| fs.createReadStream(path \[, options\]) | 創建可讀流 | 異步 | fs.promises.createReadStream(path \[, options\]) |
| fs.createWriteStream(path \[, options\]) | 創建可寫流 | 異步 | fs.promises.createWriteStream(path \[, options\]) |
| fs.watchFile(filename \[, options\], listener) | 監聽文件變化 | 異步 | \- |
| fs.unwatchFile(filename \[, listener\]) | 取消監聽文件變化 | 異步 | \- |
| fs.watch(filename \[, options\], listener) | 監聽文件系統事件 | 異步 | \- |
| fs.access(path \[, mode\], callback) | 測試用戶對 path 指定的文件或目錄的權限 | 異步 | fs.promises.access(path \[, mode\]) |
| fs.accessSync(path \[, mode\]) | 測試用戶對 path 指定的文件或目錄的權限 | 同步 | fs.promises.access(path \[, mode\]) |
>[danger] ##### 知識補充
`fs.read` 和 `fs.write` 這兩個 API 并不支持 `fs.promises` 的主要原因是它們是基于底層操作系統提供的文件讀寫接口的,而這些接口通常是異步的,因此無法直接使用 Promise 風格的 API。 這兩個 API 都是使用回調函數的方式實現異步操作的。例如,`fs.read` 方法的語法如下:
~~~javascript
fs.read(fd, buffer, offset, length, position, callback)
~~~
其中,`callback` 參數是一個回調函數,它在文件讀取完成后被調用,通常會將讀取到的數據和讀取字節數作為參數傳遞給回調函數。類似地,`fs.write` 方法也是使用回調函數的方式實現異步操作的,它的語法如下:
~~~javascript
fs.write(fd, buffer, offset, length, position, callback)
~~~
由于這兩個 API 是基于底層操作系統提供的接口實現的,因此無法直接使用 Promise 風格的 API。如果您希望使用 Promise 風格的 API,可以考慮使用 `fs.promises.readFile` 和 `fs.promises.writeFile` 這兩個方法來實現文件讀寫操作,它們都是支持 Promise 風格的異步 API。
*****
`fs.write` 和 `fs.writeFile` 兩者的主要區別在于:
1. 使用方式不同 `fs.write` 方法需要打開文件描述符,然后通過該文件描述符進行寫入操作。它的語法如下:
~~~javascript
fs.write(fd, buffer[, offset[, length[, position]]], callback)
~~~
其中,`fd` 是文件描述符,`buffer` 是一個 `Buffer` 或 `Uint8Array` 對象,`offset` 和 `length` 分別表示要寫入的數據在 `buffer` 中的偏移量和長度,`position` 表示寫入的起始位置。最后一個參數是回調函數,它在寫入完成后被調用。 相比之下,`fs.writeFile` 方法則更加簡單,只需要指定文件路徑和要寫入的數據即可。它的語法如下:
~~~javascript
fs.writeFile(file, data[, options], callback)
~~~
其中,`file` 是文件路徑,`data` 是要寫入的數據,`options` 是一個可選的配置對象,`callback` 是寫入完成后的回調函數。 2. 寫入方式不同 `fs.write` 方法是基于文件描述符進行寫入操作的,它可以使用多種寫入方式,例如:
* `fs.write(fd, buffer, offset, length, position, callback)`:向指定位置寫入數據。
* `fs.write(fd, data, callback)`:向當前文件指針位置寫入數據。
* `fs.write(fd, buffer[, offset[, length[, position]]])`:向當前文件指針位置寫入數據,并將文件指針移動到寫入數據的末尾。 相比之下,`fs.writeFile` 方法只支持一種寫入方式,即向文件中寫入指定的數據,如果文件已經存在,則會覆蓋原有的數據。 因此,如果您需要對一個已經打開的文件進行寫入操作,或者需要對一個文件進行復雜的寫入操作(例如隨機訪問或追加寫入),則應該使用 `fs.write` 方法。如果您只需要向文件中寫入指定的數據,并且不需要進行復雜的寫入操作,則可以使用 `fs.writeFile` 方法。
- 基礎
- 什么是Node.js
- 理解 I/O 模型
- 理解node 中 I/O
- 對比node 和java 使用場景
- node 模塊管理
- 內置模塊 -- buffer
- 內置模塊 -- fs
- fs -- 文件描述符
- fs -- 打開文件 api
- fs -- 文件讀取 api
- fs -- 文件寫入 api
- fs -- 創建目錄 api
- fs -- 讀取文件目錄結構 api
- fs -- 文件狀態(信息) api
- fs -- 刪除文件/目錄 api
- fs -- 重命名 api
- fs -- 復制文件 api
- 內置模塊 -- events
- 內置模塊 -- stream
- 可讀流 -- Readable
- 可寫流 -- Writable
- Duplex
- Transform
- 內置模塊 -- http
- http -- 從客戶端發起
- http -- 從服務端發起
- 內置模塊 -- url
- 網絡開發