Skip to content

Node.js 文件系统操作详解

fs 模块

javascript
const fs = require('fs');
const fsPromises = require('fs').promises;

文件读取

同步:

javascript
try {
  const data = fs.readFileSync('file.txt', 'utf8');
} catch (err) {
  console.error(err);
}

异步回调:

javascript
fs.readFile('file.txt', 'utf8', (err, data) => {
  if (err) return console.error(err);
  console.log(data);
});

Promise:

javascript
const data = await fsPromises.readFile('file.txt', 'utf8');

读取二进制文件

javascript
const image = fs.readFileSync('image.png');
// image 是 Buffer 对象
console.log(image.length); // 文件大小(字节)

文件写入

同步写入

javascript
try {
  fs.writeFileSync('output.txt', 'Hello, Node.js!', 'utf8');
  console.log('文件写入成功');
} catch (err) {
  console.error('写入文件失败:', err);
}

异步写入

javascript
// 回调方式
fs.writeFile('output.txt', 'Hello, Node.js!', 'utf8', (err) => {
  if (err) {
    console.error('写入文件失败:', err);
    return;
  }
  console.log('文件写入成功');
});

// Promise 方式
async function writeFile() {
  try {
    await fsPromises.writeFile('output.txt', 'Hello, Node.js!', 'utf8');
    console.log('文件写入成功');
  } catch (err) {
    console.error('写入文件失败:', err);
  }
}

追加写入

javascript
// 追加内容到文件末尾
fs.appendFile('log.txt', '新日志条目\n', 'utf8', (err) => {
  if (err) console.error(err);
});

// 同步方式
fs.appendFileSync('log.txt', '新日志条目\n', 'utf8');

文件信息

检查文件是否存在

javascript
// 同步方式
if (fs.existsSync('file.txt')) {
  console.log('文件存在');
}

// 异步方式(已废弃,不推荐)
// 推荐使用 fs.access 或 fsPromises.access

获取文件信息

javascript
// 同步方式
const stats = fs.statSync('file.txt');
console.log('文件大小:', stats.size);
console.log('创建时间:', stats.birthtime);
console.log('修改时间:', stats.mtime);
console.log('是文件:', stats.isFile());
console.log('是目录:', stats.isDirectory());

// 异步方式
fs.stat('file.txt', (err, stats) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log('文件信息:', stats);
});

检查访问权限

javascript
// 检查文件是否可读
fs.access('file.txt', fs.constants.R_OK, (err) => {
  if (err) {
    console.log('文件不可读');
  } else {
    console.log('文件可读');
  }
});

// 检查多个权限
fs.access('file.txt', fs.constants.R_OK | fs.constants.W_OK, (err) => {
  if (err) {
    console.log('文件不可读写');
  } else {
    console.log('文件可读写');
  }
});

目录操作

创建目录

javascript
// 同步创建
fs.mkdirSync('new-directory');

// 异步创建
fs.mkdir('new-directory', (err) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log('目录创建成功');
});

// 递归创建(Node.js 10+)
fs.mkdir('path/to/directory', { recursive: true }, (err) => {
  if (err) console.error(err);
});

读取目录

javascript
// 同步读取
const files = fs.readdirSync('.');
console.log('文件列表:', files);

// 异步读取
fs.readdir('.', (err, files) => {
  if (err) {
    console.error(err);
    return;
  }
  console.log('文件列表:', files);
});

// 读取详细信息
fs.readdir('.', { withFileTypes: true }, (err, entries) => {
  if (err) {
    console.error(err);
    return;
  }
  entries.forEach(entry => {
    if (entry.isDirectory()) {
      console.log(`目录: ${entry.name}`);
    } else {
      console.log(`文件: ${entry.name}`);
    }
  });
});

删除目录

javascript
// 删除空目录
fs.rmdir('directory', (err) => {
  if (err) console.error(err);
});

// 递归删除(Node.js 14.14+)
fs.rm('directory', { recursive: true }, (err) => {
  if (err) console.error(err);
});

文件操作

重命名/移动文件

javascript
// 重命名
fs.rename('old-name.txt', 'new-name.txt', (err) => {
  if (err) console.error(err);
});

// 移动文件
fs.rename('file.txt', 'directory/file.txt', (err) => {
  if (err) console.error(err);
});

复制文件

javascript
// 使用 fs.copyFile(Node.js 8.5+)
fs.copyFile('source.txt', 'destination.txt', (err) => {
  if (err) console.error(err);
});

// 使用流复制大文件
const fs = require('fs');
const readStream = fs.createReadStream('large-file.txt');
const writeStream = fs.createWriteStream('copy.txt');

readStream.pipe(writeStream);

readStream.on('end', () => {
  console.log('复制完成');
});

删除文件

javascript
fs.unlink('file.txt', (err) => {
  if (err) console.error(err);
  console.log('文件删除成功');
});

流处理

可读流

javascript
const readStream = fs.createReadStream('large-file.txt', {
  encoding: 'utf8',
  highWaterMark: 64 * 1024 // 64KB 缓冲区
});

readStream.on('data', (chunk) => {
  console.log('读取数据块,大小:', chunk.length);
  // 处理数据块
});

readStream.on('end', () => {
  console.log('读取完成');
});

readStream.on('error', (err) => {
  console.error('读取错误:', err);
});

可写流

javascript
const writeStream = fs.createWriteStream('output.txt', {
  encoding: 'utf8',
  flags: 'a' // 追加模式
});

writeStream.write('第一行数据\n');
writeStream.write('第二行数据\n');
writeStream.end('最后一行数据');

writeStream.on('finish', () => {
  console.log('写入完成');
});

writeStream.on('error', (err) => {
  console.error('写入错误:', err);
});

管道操作

javascript
// 基本管道
const readStream = fs.createReadStream('input.txt');
const writeStream = fs.createWriteStream('output.txt');

readStream.pipe(writeStream);

// 链式管道
const zlib = require('zlib');
readStream
  .pipe(zlib.createGzip())
  .pipe(fs.createWriteStream('output.txt.gz'));

监听文件变化

watchFile

javascript
fs.watchFile('file.txt', (curr, prev) => {
  console.log('文件已修改');
  console.log('当前大小:', curr.size);
  console.log('之前大小:', prev.size);
});

// 停止监听
fs.unwatchFile('file.txt');

watch(推荐)

javascript
const watcher = fs.watch('directory', { recursive: true }, (eventType, filename) => {
  console.log('事件类型:', eventType); // 'rename' 或 'change'
  console.log('文件名:', filename);
});

// 停止监听
watcher.close();

实用工具函数

递归读取目录

javascript
async function readDirRecursive(dir) {
  const entries = await fsPromises.readdir(dir, { withFileTypes: true });
  const files = [];
  
  for (const entry of entries) {
    const fullPath = require('path').join(dir, entry.name);
    
    if (entry.isDirectory()) {
      const subFiles = await readDirRecursive(fullPath);
      files.push(...subFiles);
    } else {
      files.push(fullPath);
    }
  }
  
  return files;
}

// 使用
readDirRecursive('./src').then(files => {
  console.log('所有文件:', files);
});

确保目录存在

javascript
async function ensureDir(dir) {
  try {
    await fsPromises.access(dir);
  } catch {
    await fsPromises.mkdir(dir, { recursive: true });
  }
}

复制目录

javascript
const path = require('path');

async function copyDir(src, dest) {
  await fsPromises.mkdir(dest, { recursive: true });
  const entries = await fsPromises.readdir(src, { withFileTypes: true });
  
  for (const entry of entries) {
    const srcPath = path.join(src, entry.name);
    const destPath = path.join(dest, entry.name);
    
    if (entry.isDirectory()) {
      await copyDir(srcPath, destPath);
    } else {
      await fsPromises.copyFile(srcPath, destPath);
    }
  }
}

总结

Node.js 文件系统操作要点:

  • 文件读写:同步、异步回调、Promise 三种方式
  • 目录操作:创建、读取、删除目录
  • 文件操作:重命名、复制、删除文件
  • 流处理:高效处理大文件
  • 文件监听:监控文件变化
  • 实用工具:递归操作、目录确保等