1.3 fs模块详解
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();