?fs 模塊詳解
Node.js 的?fs
?模塊提供了與文件系統交互的能力,是服務器端編程的核心模塊之一。它支持同步、異步(回調式)和 Promise 三種 API 風格,可滿足不同場景的需求。
1. 模塊引入
const fs = require('fs'); // 回調式 API
const fsPromises = require('fs').promises; // Promise API
const fsSync = require('fs'); // 同步 API
2. 文件讀取操作
異步回調方式
fs.readFile('example.txt', 'utf8', (err, data) => {if (err) {console.error('讀取文件失敗:', err);return;}console.log('文件內容:', data);
});
Promise 方式
fsPromises.readFile('example.txt', 'utf8').then(data => console.log('文件內容:', data)).catch(err => console.error('讀取文件失敗:', err));// 或使用 async/await
async function readFileAsync() {try {const data = await fsPromises.readFile('example.txt', 'utf8');console.log('文件內容:', data);} catch (err) {console.error('讀取文件失敗:', err);}
}
同步方式
try {const data = fsSync.readFileSync('example.txt', 'utf8');console.log('文件內容:', data);
} catch (err) {console.error('讀取文件失敗:', err);
}
3. 文件寫入操作
覆蓋寫入
// 異步
fs.writeFile('output.txt', 'Hello World!', err => {if (err) console.error('寫入失敗:', err);
});// Promise
fsPromises.writeFile('output.txt', 'Hello World!').then(() => console.log('寫入成功')).catch(err => console.error('寫入失敗:', err));// 同步
fsSync.writeFileSync('output.txt', 'Hello World!');
追加寫入
fs.appendFile('log.txt', '追加內容\n', err => { /* ... */ });
fsPromises.appendFile('log.txt', '追加內容\n');
fsSync.appendFileSync('log.txt', '追加內容\n');
4. 文件和目錄操作
創建目錄
// 遞歸創建多級目錄(如 mkdir -p)
fs.mkdir('dir/subdir', { recursive: true }, err => { /* ... */ });
fsPromises.mkdir('dir/subdir', { recursive: true });
fsSync.mkdirSync('dir/subdir', { recursive: true });
讀取目錄
fs.readdir('dir', (err, files) => {console.log('目錄內容:', files); // 返回文件名數組
});
重命名 / 移動文件
fs.rename('old.txt', 'new.txt', err => { /* ... */ });
fsPromises.rename('old.txt', 'new.txt');
fsSync.renameSync('old.txt', 'new.txt');
刪除文件 / 目錄
// 刪除文件
fs.unlink('file.txt', err => { /* ... */ });
fsPromises.unlink('file.txt');
fsSync.unlinkSync('file.txt');// 刪除目錄(遞歸刪除使用 { recursive: true })
fs.rmdir('dir', err => { /* ... */ });
fsPromises.rmdir('dir', { recursive: true });
fsSync.rmdirSync('dir', { recursive: true });
5. 文件狀態檢查
fs.stat('example.txt', (err, stats) => {if (err) return;console.log('是否為文件:', stats.isFile());console.log('是否為目錄:', stats.isDirectory());console.log('文件大小:', stats.size, '字節');console.log('創建時間:', stats.ctime);
});// Promise 版本
fsPromises.stat('example.txt').then(stats => { /* ... */ });
6. 流式操作
適合處理大文件,避免內存溢出:
讀取流
const readStream = fs.createReadStream('large_file.txt', {encoding: 'utf8',highWaterMark: 1024 * 1024 // 1MB 緩沖區
});readStream.on('data', chunk => {console.log('讀取塊:', chunk.length);
});readStream.on('end', () => console.log('讀取完成'));
readStream.on('error', err => console.error('讀取錯誤:', err));
寫入流
const writeStream = fs.createWriteStream('output.txt');writeStream.write('第一部分內容');
writeStream.write('第二部分內容');
writeStream.end(); // 結束寫入writeStream.on('finish', () => console.log('寫入完成'));
管道操作(高效復制文件)
const readStream = fs.createReadStream('input.txt');
const writeStream = fs.createWriteStream('output.txt');readStream.pipe(writeStream); // 自動處理背壓問題
7. 文件路徑處理
通常配合?path
?模塊使用:
const path = require('path');const filePath = '/home/user/dir/file.txt';
console.log('文件名:', path.basename(filePath)); // 'file.txt'
console.log('目錄:', path.dirname(filePath)); // '/home/user/dir'
console.log('擴展名:', path.extname(filePath)); // '.txt'
console.log('絕對路徑:', path.resolve('file.txt')); // 解析為絕對路徑
8. 監聽文件變化
// 監聽文件或目錄變化
fs.watch('dir', (eventType, filename) => {console.log(`文件 ${filename} 發生 ${eventType} 事件`);
});
9. 同步 vs 異步 vs Promise
類型 | 適用場景 | 特點 |
---|---|---|
同步 API | 配置加載、腳本工具 | 阻塞執行,可能導致性能問題 |
回調 API | 傳統異步場景 | 需處理回調地獄問題 |
Promise API | 現代異步代碼(async/await) | 避免回調地獄,支持鏈式調用 |
10. 最佳實踐
- 優先使用異步 API:Node.js 是單線程的,同步操作會阻塞整個事件循環。
- 錯誤處理:始終處理文件操作可能拋出的錯誤。
- 大文件處理:使用流 API 處理大文件,避免內存溢出。
- 路徑安全:使用?
path
?模塊處理路徑,避免路徑遍歷攻擊。 - Promise 化:若需使用回調 API,可用?
util.promisify
?轉換為 Promise:const { promisify } = require('util'); const readFile = promisify(fs.readFile);
11. 注意事項
- 文件權限:確保 Node.js 進程有足夠權限訪問文件。
- 跨平臺兼容性:Windows 和 Unix 系統的路徑分隔符不同,使用?
path
?模塊處理。 - 性能考量:頻繁的文件操作可能成為性能瓶頸,考慮緩存策略。
總結
fs
?模塊是 Node.js 核心能力之一,提供了豐富的文件系統操作接口。根據場景選擇合適的 API 風格(同步、回調或 Promise),并遵循最佳實踐以確保代碼健壯性和性能。對于復雜操作,推薦使用 Promise API 和?async/await
?語法提高代碼可讀性。
const fs = require('fs').promises;async function example() {try {const data = await fs.readFile('<file-path>', 'utf8');console.log(data);await fs.writeFile('<new-file-path>', '一些內容');console.log('文件已保存');} catch (err) {console.error(err);}
}example();