Node.js FS Promises:現代檔案操作的最佳實踐
自 Node.js 10 版本引入 fs/promises 以來,處理檔案系統的方式發生了革命性的變化。這套 API 讓我們能夠直接運用 Promise 與 Async/Await,徹底告別傳統的回呼地獄 (Callback Hell)。
為什麼應該全面轉向 Promises API?
- 結構扁平化:多步驟的檔案操作可以線性撰寫,邏輯極度清晰。
- 原生支援:不再需要透過
util.promisify進行轉換,減少額外開銷。 - 優雅的錯誤處理:直接使用標準的
try...catch捕捉所有異常。
基礎實戰:讀取與更新設定檔
這是開發中常見的流程:讀取 JSON、修改內容、寫回檔案。
const fs = require('fs/promises');
async function updateConfig() {
try {
// 1. 讀取並解析
const content = await fs.readFile('./config.json', 'utf8');
const config = JSON.parse(content);
// 2. 修改邏輯
config.lastLogin = new Date().toLocaleString();
config.visits += 1;
// 3. 寫回檔案 (保持排版整齊)
await fs.writeFile('./config.json', JSON.stringify(config, null, 2));
console.log('配置更新成功!');
} catch (err) {
console.error('檔案操作失敗:', err.message);
}
}
進階技巧與現代 API
1. 遞迴建立目錄
不再需要判斷資料夾是否存在,直接使用 recursive 選項即可一鍵建立整條路徑。
await fs.mkdir('./logs/2025/january', { recursive: true });
2. 檔案存取檢查 (取代 fs.exists)
使用 access 來判斷檔案是否存在或是否具有特定權限,而不是使用已廢棄的 exists。
async function isExisted(path) {
try {
await fs.access(path);
return true;
} catch (err) {
return false;
}
}
3. 使用 FileHandle 進行精細控制
對於需要多次讀寫的同個檔案,使用 open() 獲取 FileHandle 效能更好且更可控。
const handle = await fs.open('temp.txt', 'r+');
try {
const { buffer } = await handle.read(Buffer.alloc(10), 0, 10, 0);
console.log('讀到前 10 字節:', buffer.toString());
} finally {
await handle.close(); // 務必記得關閉 handle 以釋放描述符
}
常見實務:掃描目錄檔案
利用 readdir 配合 withFileTypes 選項,可以輕鬆分辨出目錄下哪些是檔案、哪些是資料夾。
async function scanDir(path) {
const entries = await fs.readdir(path, { withFileTypes: true });
for (const entry of entries) {
const type = entry.isDirectory() ? '[Dir]' : '[File]';
console.log(`${type} - ${entry.name}`);
}
}
總結
fs/promises是現代 Node.js 應用的標配。- 對於複雜的非同步流程,它能顯著降低開發維護成本。
- 雖然 Promise 很強大,但面對 GB 等級 的超大檔案,請回歸 Stream 串流模式 才是最佳方案。