Appearance
第9章:文件系统进阶
9.1 同步与异步文件操作对比
Node.js 的 fs 模块提供了同步和异步两种文件操作方式。
异步文件操作
特点:
- 不会阻塞主线程
- 性能更好,适合处理大量文件操作
- 代码更复杂,需要使用回调函数或 Promise
示例:
javascript
const fs = require('fs').promises;
// 异步读取文件
async function asyncReadFile() {
try {
const data = await fs.readFile('example.txt', 'utf8');
console.log('文件内容:', data);
} catch (err) {
console.error('读取文件失败:', err);
}
}
asyncReadFile();
console.log('其他操作...');同步文件操作
特点:
- 会阻塞主线程
- 代码更简洁,易于理解
- 适合处理少量文件操作
示例:
javascript
const fs = require('fs');
// 同步读取文件
function syncReadFile() {
try {
const data = fs.readFileSync('example.txt', 'utf8');
console.log('文件内容:', data);
} catch (err) {
console.error('读取文件失败:', err);
}
}
syncReadFile();
console.log('其他操作...');对比总结
| 操作类型 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 异步操作 | 非阻塞,性能好 | 代码复杂 | 大量文件操作,生产环境 |
| 同步操作 | 代码简单,易理解 | 阻塞主线程 | 少量文件操作,脚本工具 |
9.2 文件夹操作
创建文件夹
异步创建文件夹:
javascript
const fs = require('fs').promises;
async function createDirectory() {
try {
await fs.mkdir('new-folder', { recursive: true });
console.log('文件夹创建成功');
} catch (err) {
console.error('创建文件夹失败:', err);
}
}
createDirectory();同步创建文件夹:
javascript
const fs = require('fs');
function createDirectorySync() {
try {
fs.mkdirSync('new-folder', { recursive: true });
console.log('文件夹创建成功');
} catch (err) {
console.error('创建文件夹失败:', err);
}
}
createDirectorySync();删除文件夹
异步删除文件夹:
javascript
const fs = require('fs').promises;
async function deleteDirectory() {
try {
await fs.rm('new-folder', { recursive: true, force: true });
console.log('文件夹删除成功');
} catch (err) {
console.error('删除文件夹失败:', err);
}
}
deleteDirectory();同步删除文件夹:
javascript
const fs = require('fs');
function deleteDirectorySync() {
try {
fs.rmSync('new-folder', { recursive: true, force: true });
console.log('文件夹删除成功');
} catch (err) {
console.error('删除文件夹失败:', err);
}
}
deleteDirectorySync();遍历文件夹
异步遍历文件夹:
javascript
const fs = require('fs').promises;
const path = require('path');
async function traverseDirectory(dir) {
try {
const files = await fs.readdir(dir);
for (const file of files) {
const filePath = path.join(dir, file);
const stats = await fs.stat(filePath);
if (stats.isDirectory()) {
console.log(`目录: ${filePath}`);
await traverseDirectory(filePath); // 递归遍历子目录
} else {
console.log(`文件: ${filePath}`);
}
}
} catch (err) {
console.error('遍历文件夹失败:', err);
}
}
traverseDirectory('./');同步遍历文件夹:
javascript
const fs = require('fs');
const path = require('path');
function traverseDirectorySync(dir) {
try {
const files = fs.readdirSync(dir);
for (const file of files) {
const filePath = path.join(dir, file);
const stats = fs.statSync(filePath);
if (stats.isDirectory()) {
console.log(`目录: ${filePath}`);
traverseDirectorySync(filePath); // 递归遍历子目录
} else {
console.log(`文件: ${filePath}`);
}
}
} catch (err) {
console.error('遍历文件夹失败:', err);
}
}
traverseDirectorySync('./');9.3 文件批量操作、批量读取
批量读取文件
示例:批量读取多个文件
javascript
const fs = require('fs').promises;
async function batchReadFiles() {
const files = ['file1.txt', 'file2.txt', 'file3.txt'];
try {
// 使用 Promise.all 并行读取文件
const contents = await Promise.all(
files.map(file => fs.readFile(file, 'utf8'))
);
// 处理读取的内容
files.forEach((file, index) => {
console.log(`=== ${file} ===`);
console.log(contents[index]);
console.log('');
});
} catch (err) {
console.error('批量读取文件失败:', err);
}
}
batchReadFiles();批量写入文件
示例:批量写入多个文件
javascript
const fs = require('fs').promises;
async function batchWriteFiles() {
const files = [
{ name: 'file1.txt', content: '内容1' },
{ name: 'file2.txt', content: '内容2' },
{ name: 'file3.txt', content: '内容3' }
];
try {
// 使用 Promise.all 并行写入文件
await Promise.all(
files.map(file => fs.writeFile(file.name, file.content))
);
console.log('批量写入文件成功');
} catch (err) {
console.error('批量写入文件失败:', err);
}
}
batchWriteFiles();9.4 文件路径处理进阶
路径解析
javascript
const path = require('path');
// 解析路径
const filePath = '/home/user/project/file.js';
const parsedPath = path.parse(filePath);
console.log('解析路径:', parsedPath);
console.log('目录名:', parsedPath.dir);
console.log('文件名:', parsedPath.base);
console.log('扩展名:', parsedPath.ext);
console.log('文件名(不含扩展名):', parsedPath.name);路径拼接
javascript
const path = require('path');
// 拼接路径
const dir = '/home/user';
const subdir = 'project';
const file = 'file.js';
const fullPath = path.join(dir, subdir, file);
console.log('拼接路径:', fullPath);绝对路径
javascript
const path = require('path');
// 获取绝对路径
const relativePath = './file.js';
const absolutePath = path.resolve(relativePath);
console.log('绝对路径:', absolutePath);
// 基于当前目录获取绝对路径
const baseDir = '/home/user';
const absolutePathFromBase = path.resolve(baseDir, 'project', 'file.js');
console.log('基于基础目录的绝对路径:', absolutePathFromBase);路径规范化
javascript
const path = require('path');
// 规范化路径
const messyPath = '/home//user/../project/file.js';
const normalizedPath = path.normalize(messyPath);
console.log('规范化路径:', normalizedPath);路径比较
javascript
const path = require('path');
// 比较路径
const path1 = '/home/user/project/file.js';
const path2 = './project/file.js';
// 转换为绝对路径后比较
const absolutePath1 = path.resolve(path1);
const absolutePath2 = path.resolve(path2);
console.log('路径1:', absolutePath1);
console.log('路径2:', absolutePath2);
console.log('路径是否相同:', absolutePath1 === absolutePath2);9.5 实操案例
案例1:实现文件复制
步骤1:创建 copyFile.js 文件
javascript
// copyFile.js
const fs = require('fs').promises;
const path = require('path');
async function copyFile(source, destination) {
try {
// 读取源文件
const data = await fs.readFile(source);
// 确保目标目录存在
const destDir = path.dirname(destination);
await fs.mkdir(destDir, { recursive: true });
// 写入目标文件
await fs.writeFile(destination, data);
console.log(`文件复制成功: ${source} -> ${destination}`);
} catch (err) {
console.error('文件复制失败:', err);
}
}
// 测试
copyFile('source.txt', 'destination.txt');
copyFile('source.txt', 'subdir/destination.txt');步骤2:创建源文件
bash
echo "Hello, Node.js!" > source.txt步骤3:运行程序
bash
node copyFile.js案例2:文件夹遍历
步骤1:创建 traverse.js 文件
javascript
// traverse.js
const fs = require('fs').promises;
const path = require('path');
async function traverseDirectory(dir, indent = 0) {
try {
const files = await fs.readdir(dir);
for (const file of files) {
const filePath = path.join(dir, file);
const stats = await fs.stat(filePath);
const indentStr = ' '.repeat(indent);
if (stats.isDirectory()) {
console.log(`${indentStr}📁 ${file}/`);
await traverseDirectory(filePath, indent + 1); // 递归遍历子目录
} else {
console.log(`${indentStr}📄 ${file} (${stats.size} bytes)`);
}
}
} catch (err) {
console.error('遍历文件夹失败:', err);
}
}
// 测试
traverseDirectory('./');步骤2:运行程序
bash
node traverse.js案例3:日志写入
步骤1:创建 logger.js 文件
javascript
// logger.js
const fs = require('fs').promises;
const path = require('path');
class Logger {
constructor(logDir = './logs') {
this.logDir = logDir;
this.ensureLogDir();
}
async ensureLogDir() {
try {
await fs.mkdir(this.logDir, { recursive: true });
} catch (err) {
console.error('创建日志目录失败:', err);
}
}
async log(message, level = 'info') {
try {
const timestamp = new Date().toISOString();
const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
const logFile = path.join(this.logDir, `${new Date().toISOString().split('T')[0]}.log`);
await fs.appendFile(logFile, logMessage);
console.log(logMessage.trim());
} catch (err) {
console.error('写入日志失败:', err);
}
}
async info(message) {
await this.log(message, 'info');
}
async error(message) {
await this.log(message, 'error');
}
async warn(message) {
await this.log(message, 'warn');
}
async debug(message) {
await this.log(message, 'debug');
}
}
// 测试
const logger = new Logger();
async function testLogger() {
await logger.info('应用启动');
await logger.warn('配置文件未找到');
await logger.error('数据库连接失败');
await logger.debug('调试信息');
await logger.info('应用关闭');
}
testLogger();步骤2:运行程序
bash
node logger.js步骤3:查看日志文件
bash
ls logs/
cat logs/$(date +%Y-%m-%d).log小结
- Node.js 的
fs模块提供了同步和异步两种文件操作方式 - 异步操作非阻塞,性能更好,适合生产环境
- 同步操作代码简洁,适合少量文件操作
- 可以使用
fs模块进行文件夹的创建、删除和遍历 - 可以使用
path模块进行路径的解析、拼接和规范化 - 批量操作可以使用
Promise.all并行处理多个文件 - 实际项目中,文件系统操作是常见的需求,需要熟练掌握
现在,你已经了解了文件系统的进阶操作,接下来让我们学习 Express 框架入门。
