Node.js 文件系统

读写、目录遍历、stat、watch、以及同步/异步/流三种形态怎么选。

Intermediate
Key Points生产代码用 `node:fs/promises`;只有启动时读配置这种一次性场景才用同步 API。

三种形态

FormExampleWhen
同步fs.readFileSync启动时读配置、CLI 脚本
回调fs.readFile(path, cb)老代码
Promisefs.readFile(path)业务代码默认
Streamfs.createReadStream大文件

读写文件

JavaScript
const fs = require('node:fs/promises');

await fs.writeFile('out.txt', '第一行\n', 'utf8');
await fs.appendFile('out.txt', '第二行\n', 'utf8');

const text = await fs.readFile('out.txt', 'utf8');
console.log(text);

const buf = await fs.readFile('out.txt');   // 不传编码得到 Buffer
console.log(buf.length);
Output
第一行
第二行

18

「第一行\n」是 4 个 UTF-8 字节,「第二行\n」是 10 个(三个汉字各 3 字节 + 换行)。所以 Buffer 长度是 18,而字符串 length 只有 8。

目录

JavaScript
const fs = require('node:fs/promises');
const path = require('node:path');

// 建目录(递归,已存在不报错)
await fs.mkdir('logs/2026/10', { recursive: true });

// 列目录
const entries = await fs.readdir('.', { withFileTypes: true });
for (const e of entries) {
  console.log(e.isDirectory() ? '[D]' : '[F]', e.name);
}

// 递归遍历
async function walk(dir, out = []) {
  for (const e of await fs.readdir(dir, { withFileTypes: true })) {
    const full = path.join(dir, e.name);
    if (e.isDirectory()) await walk(full, out);
    else out.push(full);
  }
  return out;
}

stat 与判断存在

JavaScript
const fs = require('node:fs/promises');

try {
  const st = await fs.stat('package.json');
  console.log('大小', st.size, '字节');
  console.log('修改时间', st.mtime.toISOString());
  console.log('是文件', st.isFile());
} catch (e) {
  if (e.code === 'ENOENT') console.log('文件不存在');
  else throw e;
}
Do not use fs.existsSync for check-then-act — the file can vanish in between (TOCTOU race). Instead **act directly and catch ENOENT**.

监听变化

JavaScript
const fs = require('node:fs');

fs.watch('src', { recursive: true }, (event, filename) => {
  console.log(event, filename);   // change index.js
});

fs.watch behaves differently across platforms (macOS supports recursive, older Linux does not). Use chokidar in production, and remember watcher.close() or the process will not exit.

路径

JavaScript
const path = require('node:path');

console.log(path.join('a', 'b', '..', 'c'));       // a/c
console.log(path.resolve('a', 'b'));               // 绝对路径
console.log(path.basename('/x/y/z.txt'));          // z.txt
console.log(path.extname('/x/y/z.txt'));           // .txt
console.log(path.parse('/x/y/z.txt'));
Output
a/c
C:\Users\Administrator\demo\a\b
z.txt
.txt
{ root: '/', dir: '/x/y', base: 'z.txt', ext: '.txt', name: 'z' }

Friends & Partners