一、什麼是fs模塊?¶
在Node.js中,文件系統(File System)是處理文件和目錄的核心模塊,我們通常通過fs模塊來操作。想象一下,如果你用瀏覽器寫前端代碼,只能在瀏覽器中操作內存裏的數據,但Node.js是運行在服務器端的,它可以直接和電腦的硬盤打交道,而fs模塊就是Node.js提供的“硬盤操作工具包”。
比如,我們可以用它讀取本地的JSON文件、創建文件夾、寫入日誌等。fs模塊的API分爲同步和異步兩種,同步方法會阻塞代碼執行直到完成,而異步方法則不會,更適合高併發場景。
二、同步vs異步:該選哪種?¶
在開始寫代碼前,先搞清楚同步和異步的區別:
- 同步方法:像排隊買奶茶,你必須站在那等,直到拿到奶茶才能繼續。特點是簡單直接,但會阻塞後續代碼。
- 異步方法:像提前點單,下單後可以去做別的事,奶茶做好了會通知你。特點是非阻塞,效率更高,適合Node.js的單線程模型。
初學者建議:優先學習異步方法,它更符合Node.js的設計理念。如果邏輯簡單(比如讀取小文件),也可以用同步方法快速實現。
三、基礎文件讀寫操作¶
1. 引入fs模塊¶
首先,在代碼裏引入fs模塊,就像引入工具包一樣:
const fs = require('fs'); // 引入核心模塊
2. 異步讀取文件(readFile)¶
用fs.readFile方法異步讀取文件,語法如下:
fs.readFile('文件路徑', '編碼格式', (err, data) => {
// 回調函數會在讀取完成後執行
if (err) {
console.error('讀取失敗:', err);
return; // 出錯時終止後續操作
}
console.log('文件內容:', data);
});
參數解釋:
- 文件路徑:可以是相對路徑(如'./test.txt')或絕對路徑(如'/Users/yourname/test.txt')。
- 編碼格式:如'utf8'(返回字符串),不寫則返回Buffer(二進制數據)。
- 回調函數:接收兩個參數:err(錯誤信息)和data(文件內容)。
示例:創建一個test.txt文件,內容爲"Hello, Node.js!",執行以下代碼:
const fs = require('fs');
// 異步讀取test.txt(相對路徑)
fs.readFile('./test.txt', 'utf8', (err, data) => {
if (err) {
console.error('讀取失敗:', err);
return;
}
console.log('文件內容:', data); // 輸出:文件內容: Hello, Node.js!
});
3. 同步讀取文件(readFileSync)¶
如果想讓代碼“順序執行”,可以用同步方法fs.readFileSync,錯誤用try/catch捕獲:
try {
const data = fs.readFileSync('./test.txt', 'utf8');
console.log('同步讀取內容:', data);
} catch (err) {
console.error('同步讀取失敗:', err);
}
區別:同步方法會阻塞後續代碼,直到文件讀取完成。適合簡單邏輯,比如初始化配置文件時。
4. 異步寫入文件(writeFile)¶
用fs.writeFile寫入文件,默認會覆蓋已有內容(如果文件不存在則自動創建):
fs.writeFile('output.txt', '這是寫入的內容', (err) => {
if (err) {
console.error('寫入失敗:', err);
return;
}
console.log('寫入成功!');
});
示例:執行後會在當前目錄生成output.txt,內容爲"這是寫入的內容"。
注意:如果想追加內容而不是覆蓋,用fs.appendFile:
fs.appendFile('output.txt', '\n這是追加的內容', (err) => {
if (err) {
console.error('追加失敗:', err);
return;
}
console.log('追加成功!');
});
5. 路徑處理:避免“找不到文件”的坑¶
路徑是文件操作的關鍵,Node.js中path模塊可以幫我們更安全地處理路徑(如跨平臺拼接路徑)。先簡單引入path模塊:
const path = require('path');
常用路徑方法:
- path.join(__dirname, 'subdir', 'file.txt'):拼接路徑,自動處理/或\(Windows系統自動轉換)。
- __dirname:當前模塊所在目錄的絕對路徑(動態變量,永遠指向當前文件所在文件夾)。
- __filename:當前模塊的絕對路徑(包含文件名)。
示例:用path.join拼接路徑:
const fs = require('fs');
const path = require('path');
// 拼接路徑:__dirname + 'data' + 'test.txt'
const filePath = path.join(__dirname, 'data', 'test.txt');
// 讀取該路徑下的文件
fs.readFile(filePath, 'utf8', (err, data) => {
if (err) {
console.error('路徑錯誤:', err);
return;
}
console.log('正確讀取到文件:', data);
});
四、目錄操作:創建、讀取、刪除¶
1. 創建目錄(mkdir)¶
用fs.mkdir創建目錄,默認只創建最外層目錄,嵌套目錄需要遞歸創建:
fs.mkdir('./newDir', (err) => {
if (err) {
console.error('創建目錄失敗:', err);
return;
}
console.log('目錄創建成功!');
});
遞歸創建多級目錄:需要加recursive: true參數:
fs.mkdir('./a/b/c', { recursive: true }, (err) => { // 允許創建嵌套目錄
if (err) {
console.error('創建多級目錄失敗:', err);
return;
}
console.log('多級目錄創建成功!');
});
2. 讀取目錄內容(readdir)¶
用fs.readdir列出目錄下的所有文件和子目錄:
fs.readdir('./newDir', (err, files) => {
if (err) {
console.error('讀取目錄失敗:', err);
return;
}
console.log('目錄內容:', files); // 輸出數組,如 ['file1.txt', 'subDir']
});
3. 刪除目錄(rmdir)¶
用fs.rmdir刪除目錄,僅支持刪除空目錄:
fs.rmdir('./emptyDir', (err) => { // 確保目錄爲空才能刪除
if (err) {
console.error('刪除目錄失敗:', err);
return;
}
console.log('目錄刪除成功!');
});
五、文件信息與權限檢查¶
1. 檢查文件是否存在(existsSync)¶
用fs.existsSync快速檢查文件/目錄是否存在(同步方法):
const fs = require('fs');
const path = require('path');
const filePath = path.join(__dirname, 'test.txt');
if (fs.existsSync(filePath)) {
console.log('文件存在!');
fs.readFile(filePath, 'utf8', (err, data) => { /* ... */ });
} else {
console.log('文件不存在!');
}
2. 獲取文件/目錄信息(stat)¶
用fs.stat獲取文件詳細信息(大小、創建時間、是否爲目錄等):
fs.stat('./test.txt', (err, stats) => {
if (err) {
console.error('獲取信息失敗:', err);
return;
}
console.log('是否爲文件:', stats.isFile()); // true
console.log('文件大小:', stats.size, '字節'); // 15字節(示例)
});
六、常見問題與解決¶
1. 文件路徑錯誤怎麼辦?¶
原因:相對路徑的基準是“執行node命令的目錄”,而非腳本所在目錄。
解決:用__dirname(當前腳本所在目錄)拼接路徑,或直接用絕對路徑。
// 錯誤示例(可能找不到文件)
fs.readFile('test.txt', ...); // 依賴執行node命令的位置
// 正確示例
fs.readFile(path.join(__dirname, 'test.txt'), ...); // 基於腳本位置的絕對路徑
2. 如何處理大文件?¶
小文件用readFile/writeFile即可,但大文件(如視頻、日誌)會佔用內存。此時推薦使用流(Stream),它會分塊讀取數據:
const fs = require('fs');
const readStream = fs.createReadStream('largeFile.txt');
const writeStream = fs.createWriteStream('copiedFile.txt');
// 管道流:數據從readStream流向writeStream
readStream.pipe(writeStream);
七、總結¶
fs模塊是Node.js操作文件系統的基石,掌握它你就能實現:
- 讀取配置文件、日誌文件
- 創建/刪除目錄、文件
- 處理文件內容(讀寫、追加、複製)
實踐建議:
1. 先從簡單的“創建文件→寫入內容→讀取內容→刪除文件”開始
2. 嘗試用path模塊處理不同平臺的路徑問題
3. 對比同步/異步方法的區別,理解事件循環和非阻塞I/O的優勢
記住,編程學習沒有捷徑,多敲代碼、多踩坑、多調試,才能真正掌握文件系統操作的精髓!