Skip to content

Node.js文件系统2024:fs模块同步异步操作完整指南

📊 SEO元描述:2024年最新Node.js文件系统教程,详解fs模块同步异步操作、文件读写、目录操作、文件流处理、监听文件变化。包含完整代码示例,适合Node.js开发者掌握文件系统核心技能。

核心关键词:Node.js文件系统、fs模块、文件读写、目录操作、文件流、异步文件操作、Node.js文件处理

长尾关键词:Node.js怎么读写文件、fs模块同步异步区别、Node.js目录操作、文件流处理技巧、监听文件变化方法


📚 Node.js文件系统学习目标与核心收获

通过本节Node.js文件系统模块详解的学习,你将系统性掌握:

  • 同步异步操作对比:理解fs模块同步和异步操作的区别和选择策略
  • 文件读写精通:掌握各种文件读写方式和编码处理技巧
  • 目录操作技能:学会目录创建、遍历、删除等复杂目录操作
  • 文件流处理:掌握大文件处理、流式读写和管道操作
  • 文件监控能力:学会监听文件变化和实现热重载功能
  • 错误处理策略:建立完善的文件操作错误处理和重试机制

🎯 适合人群

  • Node.js开发者需要掌握文件系统操作和数据处理
  • 后端工程师需要处理文件上传、下载、存储等功能
  • 工具开发者需要开发文件处理和管理工具
  • 系统管理员需要了解Node.js文件操作的性能特点

🌟 同步vs异步操作:性能与阻塞的权衡

什么时候使用同步操作,什么时候使用异步操作?这是Node.js文件系统操作中的核心问题。同步操作简单直接但会阻塞事件循环,异步操作不阻塞但需要处理回调。理解两者的性能影响和适用场景是高效文件处理的关键。

同步vs异步操作特点

  • 🎯 同步操作:代码简单、会阻塞、适合启动时配置加载
  • 🔧 异步操作:不阻塞、代码复杂、适合运行时文件处理
  • 💡 Promise版本:结合异步优势和简洁语法的现代选择
  • 📚 性能考虑:大文件操作必须使用异步避免阻塞
  • 🚀 错误处理:异步操作需要更完善的错误处理机制

💡 选择原则:应用启动时可以使用同步操作加载配置,运行时处理用户请求必须使用异步操作。

同步异步操作对比和最佳实践

javascript
// 🎉 fs模块同步异步操作完整对比

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

// === 同步操作示例 ===
class SyncFileOperations {
    // 同步读取配置文件(适合应用启动时)
    static loadConfigSync() {
        console.log('=== 同步文件操作演示 ===');
        
        try {
            console.log('开始同步读取配置文件...');
            const startTime = Date.now();
            
            // 同步读取文件
            const configData = fs.readFileSync('package.json', 'utf8');
            const config = JSON.parse(configData);
            
            const endTime = Date.now();
            console.log(`✅ 同步读取完成,耗时: ${endTime - startTime}ms`);
            
            return {
                name: config.name,
                version: config.version,
                loadTime: endTime - startTime
            };
        } catch (error) {
            console.error('❌ 同步读取失败:', error.message);
            throw error;
        }
    }
    
    // 同步文件操作的问题演示
    static demonstrateBlockingIssue() {
        console.log('\n=== 同步操作阻塞问题演示 ===');
        
        // 启动一个定时器观察阻塞
        const timer = setInterval(() => {
            console.log('⏰ 定时器执行:', new Date().toLocaleTimeString());
        }, 100);
        
        console.log('开始同步操作...');
        
        try {
            // 模拟大文件读取(会阻塞)
            for (let i = 0; i < 5; i++) {
                const data = fs.readFileSync('package.json', 'utf8');
                // 模拟处理时间
                const start = Date.now();
                while (Date.now() - start < 200) {
                    // 忙等待,模拟CPU密集操作
                }
                console.log(`同步操作 ${i + 1} 完成`);
            }
        } catch (error) {
            console.error('同步操作失败:', error.message);
        }
        
        console.log('同步操作全部完成');
        
        // 清理定时器
        setTimeout(() => {
            clearInterval(timer);
            console.log('注意:在同步操作期间,定时器被阻塞了');
        }, 1000);
    }
    
    // 同步目录操作
    static syncDirectoryOperations() {
        console.log('\n=== 同步目录操作 ===');
        
        const testDir = './test-sync-dir';
        
        try {
            // 创建目录
            if (!fs.existsSync(testDir)) {
                fs.mkdirSync(testDir, { recursive: true });
                console.log('✅ 同步创建目录成功');
            }
            
            // 写入测试文件
            const testFiles = ['file1.txt', 'file2.txt', 'file3.txt'];
            testFiles.forEach((filename, index) => {
                const filePath = path.join(testDir, filename);
                const content = `这是测试文件 ${index + 1}\n创建时间: ${new Date().toISOString()}`;
                fs.writeFileSync(filePath, content, 'utf8');
                console.log(`✅ 创建文件: ${filename}`);
            });
            
            // 读取目录内容
            const files = fs.readdirSync(testDir);
            console.log('目录内容:', files);
            
            // 获取文件信息
            files.forEach(filename => {
                const filePath = path.join(testDir, filename);
                const stats = fs.statSync(filePath);
                console.log(`📄 ${filename}: ${stats.size} bytes, 修改时间: ${stats.mtime.toISOString()}`);
            });
            
            return { directory: testDir, files: files.length };
        } catch (error) {
            console.error('❌ 同步目录操作失败:', error.message);
            throw error;
        }
    }
}

// === 异步操作示例 ===
class AsyncFileOperations {
    // 异步读取文件(回调方式)
    static loadConfigAsync(callback) {
        console.log('\n=== 异步文件操作演示(回调) ===');
        
        console.log('开始异步读取配置文件...');
        const startTime = Date.now();
        
        fs.readFile('package.json', 'utf8', (error, data) => {
            if (error) {
                console.error('❌ 异步读取失败:', error.message);
                callback(error, null);
                return;
            }
            
            try {
                const config = JSON.parse(data);
                const endTime = Date.now();
                console.log(`✅ 异步读取完成,耗时: ${endTime - startTime}ms`);
                
                const result = {
                    name: config.name,
                    version: config.version,
                    loadTime: endTime - startTime
                };
                
                callback(null, result);
            } catch (parseError) {
                console.error('❌ JSON解析失败:', parseError.message);
                callback(parseError, null);
            }
        });
        
        console.log('异步读取请求已发出,继续执行其他代码...');
    }
    
    // 异步操作不阻塞演示
    static demonstrateNonBlocking() {
        console.log('\n=== 异步操作非阻塞演示 ===');
        
        // 启动定时器观察非阻塞特性
        const timer = setInterval(() => {
            console.log('⏰ 定时器执行:', new Date().toLocaleTimeString());
        }, 100);
        
        console.log('开始异步操作...');
        
        // 并行执行多个异步文件操作
        for (let i = 0; i < 5; i++) {
            fs.readFile('package.json', 'utf8', (error, data) => {
                if (error) {
                    console.error(`异步操作 ${i + 1} 失败:`, error.message);
                    return;
                }
                
                // 模拟处理时间
                setTimeout(() => {
                    console.log(`✅ 异步操作 ${i + 1} 完成`);
                }, Math.random() * 200);
            });
        }
        
        console.log('所有异步操作已启动,主线程继续执行...');
        
        // 清理定时器
        setTimeout(() => {
            clearInterval(timer);
            console.log('注意:异步操作期间,定时器正常执行,没有被阻塞');
        }, 2000);
    }
    
    // 异步目录操作
    static asyncDirectoryOperations(callback) {
        console.log('\n=== 异步目录操作 ===');
        
        const testDir = './test-async-dir';
        
        // 检查目录是否存在
        fs.access(testDir, fs.constants.F_OK, (accessError) => {
            if (accessError) {
                // 目录不存在,创建目录
                fs.mkdir(testDir, { recursive: true }, (mkdirError) => {
                    if (mkdirError) {
                        console.error('❌ 创建目录失败:', mkdirError.message);
                        callback(mkdirError, null);
                        return;
                    }
                    
                    console.log('✅ 异步创建目录成功');
                    this.createTestFiles(testDir, callback);
                });
            } else {
                console.log('目录已存在');
                this.createTestFiles(testDir, callback);
            }
        });
    }
    
    static createTestFiles(testDir, callback) {
        const testFiles = ['async1.txt', 'async2.txt', 'async3.txt'];
        let completedFiles = 0;
        const results = [];
        
        testFiles.forEach((filename, index) => {
            const filePath = path.join(testDir, filename);
            const content = `这是异步测试文件 ${index + 1}\n创建时间: ${new Date().toISOString()}`;
            
            fs.writeFile(filePath, content, 'utf8', (writeError) => {
                if (writeError) {
                    console.error(`❌ 创建文件 ${filename} 失败:`, writeError.message);
                    results.push({ filename, error: writeError.message });
                } else {
                    console.log(`✅ 创建文件: ${filename}`);
                    results.push({ filename, success: true });
                }
                
                completedFiles++;
                
                if (completedFiles === testFiles.length) {
                    // 所有文件创建完成,读取目录内容
                    fs.readdir(testDir, (readdirError, files) => {
                        if (readdirError) {
                            console.error('❌ 读取目录失败:', readdirError.message);
                            callback(readdirError, null);
                            return;
                        }
                        
                        console.log('异步目录内容:', files);
                        callback(null, { directory: testDir, files: files.length, results });
                    });
                }
            });
        });
    }
}

// === Promise版本异步操作 ===
class PromiseFileOperations {
    // Promise版本文件操作
    static async loadConfigPromise() {
        console.log('\n=== Promise版本文件操作演示 ===');
        
        try {
            console.log('开始Promise读取配置文件...');
            const startTime = Date.now();
            
            const data = await fsPromises.readFile('package.json', 'utf8');
            const config = JSON.parse(data);
            
            const endTime = Date.now();
            console.log(`✅ Promise读取完成,耗时: ${endTime - startTime}ms`);
            
            return {
                name: config.name,
                version: config.version,
                loadTime: endTime - startTime
            };
        } catch (error) {
            console.error('❌ Promise读取失败:', error.message);
            throw error;
        }
    }
    
    // 并行Promise操作
    static async parallelPromiseOperations() {
        console.log('\n=== 并行Promise操作演示 ===');
        
        try {
            const startTime = Date.now();
            
            // 并行读取多个文件
            const [packageData, readmeData] = await Promise.all([
                fsPromises.readFile('package.json', 'utf8').catch(err => ({ error: err.message })),
                fsPromises.readFile('README.md', 'utf8').catch(err => ({ error: err.message }))
            ]);
            
            const endTime = Date.now();
            console.log(`✅ 并行读取完成,耗时: ${endTime - startTime}ms`);
            
            return {
                package: typeof packageData === 'string' ? JSON.parse(packageData) : packageData,
                readme: typeof readmeData === 'string' ? { size: readmeData.length } : readmeData,
                totalTime: endTime - startTime
            };
        } catch (error) {
            console.error('❌ 并行Promise操作失败:', error.message);
            throw error;
        }
    }
    
    // Promise目录操作
    static async promiseDirectoryOperations() {
        console.log('\n=== Promise目录操作演示 ===');
        
        const testDir = './test-promise-dir';
        
        try {
            // 确保目录存在
            await fsPromises.mkdir(testDir, { recursive: true });
            console.log('✅ Promise创建目录成功');
            
            // 并行创建多个文件
            const testFiles = ['promise1.txt', 'promise2.txt', 'promise3.txt'];
            const filePromises = testFiles.map((filename, index) => {
                const filePath = path.join(testDir, filename);
                const content = `这是Promise测试文件 ${index + 1}\n创建时间: ${new Date().toISOString()}`;
                return fsPromises.writeFile(filePath, content, 'utf8');
            });
            
            await Promise.all(filePromises);
            console.log('✅ 所有文件创建完成');
            
            // 读取目录内容
            const files = await fsPromises.readdir(testDir);
            console.log('Promise目录内容:', files);
            
            // 获取文件详细信息
            const fileStats = await Promise.all(
                files.map(async (filename) => {
                    const filePath = path.join(testDir, filename);
                    const stats = await fsPromises.stat(filePath);
                    return {
                        name: filename,
                        size: stats.size,
                        modified: stats.mtime.toISOString(),
                        isFile: stats.isFile()
                    };
                })
            );
            
            console.log('文件详细信息:', fileStats);
            
            return {
                directory: testDir,
                files: files.length,
                details: fileStats
            };
        } catch (error) {
            console.error('❌ Promise目录操作失败:', error.message);
            throw error;
        }
    }
}

// === 性能对比测试 ===
class PerformanceComparison {
    static async comparePerformance() {
        console.log('\n=== 文件操作性能对比 ===');
        
        const iterations = 10;
        const testFile = 'package.json';
        
        // 同步操作性能测试
        console.log('🔄 测试同步操作性能...');
        const syncStartTime = Date.now();
        
        for (let i = 0; i < iterations; i++) {
            try {
                fs.readFileSync(testFile, 'utf8');
            } catch (error) {
                console.error(`同步操作 ${i + 1} 失败:`, error.message);
            }
        }
        
        const syncEndTime = Date.now();
        const syncDuration = syncEndTime - syncStartTime;
        
        // 异步操作性能测试
        console.log('🔄 测试异步操作性能...');
        const asyncStartTime = Date.now();
        
        const asyncPromises = [];
        for (let i = 0; i < iterations; i++) {
            asyncPromises.push(
                fsPromises.readFile(testFile, 'utf8').catch(error => {
                    console.error(`异步操作 ${i + 1} 失败:`, error.message);
                    return null;
                })
            );
        }
        
        await Promise.all(asyncPromises);
        const asyncEndTime = Date.now();
        const asyncDuration = asyncEndTime - asyncStartTime;
        
        // 结果对比
        const comparison = {
            iterations,
            sync: {
                duration: syncDuration,
                averagePerOperation: (syncDuration / iterations).toFixed(2),
                blocking: true
            },
            async: {
                duration: asyncDuration,
                averagePerOperation: (asyncDuration / iterations).toFixed(2),
                blocking: false
            },
            speedup: (syncDuration / asyncDuration).toFixed(2)
        };
        
        console.log('\n📊 性能对比结果:');
        console.log(JSON.stringify(comparison, null, 2));
        
        return comparison;
    }
}

// 运行演示
(async () => {
    try {
        // 同步操作演示
        const syncConfig = SyncFileOperations.loadConfigSync();
        console.log('同步配置结果:', syncConfig);
        
        SyncFileOperations.demonstrateBlockingIssue();
        
        setTimeout(async () => {
            const syncDirResult = SyncFileOperations.syncDirectoryOperations();
            console.log('同步目录操作结果:', syncDirResult);
            
            // 异步操作演示
            AsyncFileOperations.loadConfigAsync((error, result) => {
                if (error) {
                    console.error('异步配置加载失败:', error.message);
                } else {
                    console.log('异步配置结果:', result);
                }
            });
            
            AsyncFileOperations.demonstrateNonBlocking();
            
            AsyncFileOperations.asyncDirectoryOperations((error, result) => {
                if (error) {
                    console.error('异步目录操作失败:', error.message);
                } else {
                    console.log('异步目录操作结果:', result);
                }
            });
            
            // Promise操作演示
            const promiseConfig = await PromiseFileOperations.loadConfigPromise();
            console.log('Promise配置结果:', promiseConfig);
            
            const parallelResult = await PromiseFileOperations.parallelPromiseOperations();
            console.log('并行Promise结果:', parallelResult);
            
            const promiseDirResult = await PromiseFileOperations.promiseDirectoryOperations();
            console.log('Promise目录操作结果:', promiseDirResult);
            
            // 性能对比
            const performanceResult = await PerformanceComparison.comparePerformance();
            console.log('性能对比完成');
            
        }, 3000);
        
    } catch (error) {
        console.error('演示执行失败:', error.message);
    }
})();

// 导出模块
if (typeof module !== 'undefined' && module.exports) {
    module.exports = {
        SyncFileOperations,
        AsyncFileOperations,
        PromiseFileOperations,
        PerformanceComparison
    };
}

文件读写操作详解

文件读写是文件系统操作的核心,需要掌握不同的读写方式和编码处理。

javascript
// 🎉 文件读写操作完整示例

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

// === 高级文件读写操作 ===
class AdvancedFileOperations {
    constructor(basePath = './') {
        this.basePath = basePath;
    }
    
    // 安全文件读取(带重试机制)
    async safeReadFile(filename, options = {}) {
        const {
            encoding = 'utf8',
            maxRetries = 3,
            retryDelay = 1000,
            timeout = 5000
        } = options;
        
        const filePath = path.resolve(this.basePath, filename);
        
        for (let attempt = 1; attempt <= maxRetries; attempt++) {
            try {
                console.log(`📖 尝试读取文件 (第${attempt}次): ${filename}`);
                
                // 设置超时
                const readPromise = fsPromises.readFile(filePath, encoding);
                const timeoutPromise = new Promise((_, reject) => {
                    setTimeout(() => reject(new Error('读取超时')), timeout);
                });
                
                const data = await Promise.race([readPromise, timeoutPromise]);
                console.log(`✅ 文件读取成功: ${filename}`);
                
                return {
                    success: true,
                    data,
                    size: Buffer.byteLength(data, encoding),
                    attempts: attempt
                };
            } catch (error) {
                console.error(`❌ 第${attempt}次读取失败:`, error.message);
                
                if (attempt === maxRetries) {
                    throw new Error(`文件读取失败,已重试${maxRetries}次: ${error.message}`);
                }
                
                // 等待后重试
                await new Promise(resolve => setTimeout(resolve, retryDelay));
                retryDelay *= 2; // 指数退避
            }
        }
    }
    
    // 安全文件写入(原子操作)
    async safeWriteFile(filename, data, options = {}) {
        const {
            encoding = 'utf8',
            backup = true,
            atomic = true
        } = options;
        
        const filePath = path.resolve(this.basePath, filename);
        const tempPath = filePath + '.tmp';
        const backupPath = filePath + '.backup';
        
        try {
            // 创建备份
            if (backup) {
                try {
                    await fsPromises.access(filePath);
                    await fsPromises.copyFile(filePath, backupPath);
                    console.log(`📋 创建备份: ${filename}.backup`);
                } catch (error) {
                    // 原文件不存在,无需备份
                }
            }
            
            if (atomic) {
                // 原子写入:先写临时文件,再重命名
                await fsPromises.writeFile(tempPath, data, encoding);
                await fsPromises.rename(tempPath, filePath);
                console.log(`✅ 原子写入完成: ${filename}`);
            } else {
                // 直接写入
                await fsPromises.writeFile(filePath, data, encoding);
                console.log(`✅ 直接写入完成: ${filename}`);
            }
            
            return {
                success: true,
                size: Buffer.byteLength(data, encoding),
                atomic,
                backup
            };
        } catch (error) {
            // 清理临时文件
            try {
                await fsPromises.unlink(tempPath);
            } catch (cleanupError) {
                // 忽略清理错误
            }
            
            console.error(`❌ 文件写入失败: ${filename}`, error.message);
            throw error;
        }
    }
    
    // 文件追加操作
    async appendToFile(filename, data, options = {}) {
        const {
            encoding = 'utf8',
            separator = '\n',
            maxSize = 10 * 1024 * 1024 // 10MB
        } = options;
        
        const filePath = path.resolve(this.basePath, filename);
        
        try {
            // 检查文件大小
            try {
                const stats = await fsPromises.stat(filePath);
                if (stats.size > maxSize) {
                    throw new Error(`文件过大 (${stats.size} bytes),超过限制 (${maxSize} bytes)`);
                }
            } catch (statError) {
                if (statError.code !== 'ENOENT') {
                    throw statError;
                }
                // 文件不存在,继续执行
            }
            
            const appendData = separator + data;
            await fsPromises.appendFile(filePath, appendData, encoding);
            
            console.log(`✅ 数据追加成功: ${filename}`);
            
            return {
                success: true,
                appendedSize: Buffer.byteLength(appendData, encoding)
            };
        } catch (error) {
            console.error(`❌ 数据追加失败: ${filename}`, error.message);
            throw error;
        }
    }
    
    // 分块读取大文件
    async readFileInChunks(filename, options = {}) {
        const {
            chunkSize = 64 * 1024, // 64KB
            encoding = 'utf8',
            onChunk = null
        } = options;
        
        const filePath = path.resolve(this.basePath, filename);
        
        try {
            const stats = await fsPromises.stat(filePath);
            const fileSize = stats.size;
            const chunks = [];
            
            console.log(`📖 开始分块读取文件: ${filename} (${fileSize} bytes)`);
            
            const fileHandle = await fsPromises.open(filePath, 'r');
            
            try {
                let position = 0;
                let chunkIndex = 0;
                
                while (position < fileSize) {
                    const remainingBytes = fileSize - position;
                    const currentChunkSize = Math.min(chunkSize, remainingBytes);
                    
                    const buffer = Buffer.alloc(currentChunkSize);
                    const { bytesRead } = await fileHandle.read(buffer, 0, currentChunkSize, position);
                    
                    const chunkData = buffer.slice(0, bytesRead).toString(encoding);
                    chunks.push(chunkData);
                    
                    if (onChunk) {
                        onChunk(chunkData, chunkIndex, position, fileSize);
                    }
                    
                    position += bytesRead;
                    chunkIndex++;
                    
                    console.log(`📄 读取块 ${chunkIndex}: ${bytesRead} bytes (${((position / fileSize) * 100).toFixed(1)}%)`);
                }
                
                console.log(`✅ 分块读取完成: ${chunkIndex} 个块`);
                
                return {
                    success: true,
                    chunks,
                    totalChunks: chunkIndex,
                    totalSize: fileSize,
                    data: chunks.join('')
                };
            } finally {
                await fileHandle.close();
            }
        } catch (error) {
            console.error(`❌ 分块读取失败: ${filename}`, error.message);
            throw error;
        }
    }
    
    // 文件比较
    async compareFiles(file1, file2, options = {}) {
        const {
            algorithm = 'md5',
            chunkSize = 64 * 1024
        } = options;
        
        const crypto = require('crypto');
        
        try {
            console.log(`🔍 比较文件: ${file1} vs ${file2}`);
            
            // 首先比较文件大小
            const [stats1, stats2] = await Promise.all([
                fsPromises.stat(path.resolve(this.basePath, file1)),
                fsPromises.stat(path.resolve(this.basePath, file2))
            ]);
            
            if (stats1.size !== stats2.size) {
                return {
                    identical: false,
                    reason: 'size_different',
                    file1Size: stats1.size,
                    file2Size: stats2.size
                };
            }
            
            // 计算文件哈希
            const [hash1, hash2] = await Promise.all([
                this.calculateFileHash(file1, algorithm),
                this.calculateFileHash(file2, algorithm)
            ]);
            
            const identical = hash1 === hash2;
            
            console.log(`${identical ? '✅' : '❌'} 文件比较结果: ${identical ? '相同' : '不同'}`);
            
            return {
                identical,
                reason: identical ? 'identical' : 'content_different',
                file1Hash: hash1,
                file2Hash: hash2,
                algorithm,
                size: stats1.size
            };
        } catch (error) {
            console.error('❌ 文件比较失败:', error.message);
            throw error;
        }
    }
    
    // 计算文件哈希
    async calculateFileHash(filename, algorithm = 'md5') {
        const crypto = require('crypto');
        const filePath = path.resolve(this.basePath, filename);
        
        return new Promise((resolve, reject) => {
            const hash = crypto.createHash(algorithm);
            const stream = fs.createReadStream(filePath);
            
            stream.on('data', (data) => {
                hash.update(data);
            });
            
            stream.on('end', () => {
                resolve(hash.digest('hex'));
            });
            
            stream.on('error', (error) => {
                reject(error);
            });
        });
    }
    
    // 文件加密写入
    async writeEncryptedFile(filename, data, password, options = {}) {
        const crypto = require('crypto');
        const {
            algorithm = 'aes-256-gcm',
            encoding = 'utf8'
        } = options;
        
        try {
            // 生成密钥和IV
            const key = crypto.scryptSync(password, 'salt', 32);
            const iv = crypto.randomBytes(16);
            
            // 创建加密器
            const cipher = crypto.createCipher(algorithm, key);
            
            // 加密数据
            let encrypted = cipher.update(data, encoding, 'hex');
            encrypted += cipher.final('hex');
            
            // 保存加密数据和IV
            const encryptedData = {
                algorithm,
                iv: iv.toString('hex'),
                data: encrypted
            };
            
            await this.safeWriteFile(filename, JSON.stringify(encryptedData), { encoding: 'utf8' });
            
            console.log(`🔐 加密文件写入成功: ${filename}`);
            
            return {
                success: true,
                algorithm,
                encryptedSize: encrypted.length
            };
        } catch (error) {
            console.error(`❌ 加密文件写入失败: ${filename}`, error.message);
            throw error;
        }
    }
    
    // 文件解密读取
    async readEncryptedFile(filename, password, options = {}) {
        const crypto = require('crypto');
        const { encoding = 'utf8' } = options;
        
        try {
            const encryptedContent = await this.safeReadFile(filename, { encoding: 'utf8' });
            const encryptedData = JSON.parse(encryptedContent.data);
            
            // 重建密钥
            const key = crypto.scryptSync(password, 'salt', 32);
            
            // 创建解密器
            const decipher = crypto.createDecipher(encryptedData.algorithm, key);
            
            // 解密数据
            let decrypted = decipher.update(encryptedData.data, 'hex', encoding);
            decrypted += decipher.final(encoding);
            
            console.log(`🔓 加密文件读取成功: ${filename}`);
            
            return {
                success: true,
                data: decrypted,
                algorithm: encryptedData.algorithm
            };
        } catch (error) {
            console.error(`❌ 加密文件读取失败: ${filename}`, error.message);
            throw error;
        }
    }
}

// === JSON文件操作工具 ===
class JSONFileManager extends AdvancedFileOperations {
    // 读取JSON文件
    async readJSON(filename, options = {}) {
        const { defaultValue = null, validate = null } = options;
        
        try {
            const result = await this.safeReadFile(filename);
            const jsonData = JSON.parse(result.data);
            
            if (validate && typeof validate === 'function') {
                const isValid = validate(jsonData);
                if (!isValid) {
                    throw new Error('JSON数据验证失败');
                }
            }
            
            console.log(`✅ JSON文件读取成功: ${filename}`);
            return jsonData;
        } catch (error) {
            if (error.code === 'ENOENT' && defaultValue !== null) {
                console.log(`📄 文件不存在,返回默认值: ${filename}`);
                return defaultValue;
            }
            
            console.error(`❌ JSON文件读取失败: ${filename}`, error.message);
            throw error;
        }
    }
    
    // 写入JSON文件
    async writeJSON(filename, data, options = {}) {
        const { 
            indent = 2, 
            validate = null,
            backup = true 
        } = options;
        
        try {
            if (validate && typeof validate === 'function') {
                const isValid = validate(data);
                if (!isValid) {
                    throw new Error('JSON数据验证失败');
                }
            }
            
            const jsonString = JSON.stringify(data, null, indent);
            await this.safeWriteFile(filename, jsonString, { backup });
            
            console.log(`✅ JSON文件写入成功: ${filename}`);
            return {
                success: true,
                size: jsonString.length
            };
        } catch (error) {
            console.error(`❌ JSON文件写入失败: ${filename}`, error.message);
            throw error;
        }
    }
    
    // 更新JSON文件
    async updateJSON(filename, updater, options = {}) {
        const { createIfNotExists = true, defaultValue = {} } = options;
        
        try {
            let currentData;
            
            try {
                currentData = await this.readJSON(filename);
            } catch (error) {
                if (error.code === 'ENOENT' && createIfNotExists) {
                    currentData = defaultValue;
                } else {
                    throw error;
                }
            }
            
            const updatedData = typeof updater === 'function' 
                ? updater(currentData) 
                : { ...currentData, ...updater };
            
            await this.writeJSON(filename, updatedData, options);
            
            console.log(`✅ JSON文件更新成功: ${filename}`);
            return updatedData;
        } catch (error) {
            console.error(`❌ JSON文件更新失败: ${filename}`, error.message);
            throw error;
        }
    }
}

// 使用示例
(async () => {
    try {
        const fileOps = new AdvancedFileOperations('./test-files');
        const jsonManager = new JSONFileManager('./test-files');
        
        // 确保测试目录存在
        await fsPromises.mkdir('./test-files', { recursive: true });
        
        // 安全文件读写演示
        await fileOps.safeWriteFile('test.txt', '这是测试内容\n第二行内容', { backup: true });
        const readResult = await fileOps.safeReadFile('test.txt');
        console.log('读取结果:', readResult);
        
        // 文件追加演示
        await fileOps.appendToFile('test.txt', '追加的内容');
        
        // JSON文件操作演示
        const testData = {
            name: 'Node.js学习',
            version: '1.0.0',
            features: ['文件操作', 'JSON处理', '错误处理'],
            config: {
                debug: true,
                maxRetries: 3
            }
        };
        
        await jsonManager.writeJSON('config.json', testData);
        const configData = await jsonManager.readJSON('config.json');
        console.log('JSON配置:', configData);
        
        // 更新JSON文件
        await jsonManager.updateJSON('config.json', (data) => ({
            ...data,
            lastUpdated: new Date().toISOString(),
            config: {
                ...data.config,
                debug: false
            }
        }));
        
        console.log('高级文件操作演示完成');
        
    } catch (error) {
        console.error('演示执行失败:', error.message);
    }
})();

// 导出模块
if (typeof module !== 'undefined' && module.exports) {
    module.exports = {
        AdvancedFileOperations,
        JSONFileManager
    };
}

📚 Node.js文件系统学习总结与下一步规划

✅ 本节核心收获回顾

通过本节Node.js文件系统模块详解的学习,你已经掌握:

  1. 同步异步操作精通:理解fs模块不同操作方式的性能影响和选择策略
  2. 文件读写技能:掌握安全文件操作、分块读取、加密存储等高级技巧
  3. 错误处理策略:建立了完善的文件操作错误处理和重试机制
  4. 性能优化能力:学会选择合适的文件操作方式优化应用性能
  5. 实用工具开发:能够开发文件管理、JSON处理等实用工具

🎯 Node.js学习下一步

  1. 目录操作深入:学习复杂目录遍历、批量操作、权限管理
  2. 文件流处理:掌握Stream API进行大文件和实时数据处理
  3. 文件监控系统:实现文件变化监听和热重载功能
  4. 路径模块应用:学习跨平台路径处理和文件系统导航

🔗 相关学习资源

💪 实践练习建议

  1. 文件管理工具:开发一个完整的文件管理和备份工具
  2. 日志系统实现:创建高性能的日志文件写入和轮转系统
  3. 配置管理器:实现支持热重载的配置文件管理系统
  4. 文件同步工具:开发文件同步和版本控制工具

🔍 常见问题FAQ

Q1: 什么时候使用同步文件操作?

A: 仅在应用启动时加载配置文件或初始化数据时使用同步操作。运行时处理用户请求必须使用异步操作,避免阻塞事件循环。

Q2: 如何处理大文件读写?

A: 使用Stream API或分块读取方式处理大文件。避免一次性将整个文件加载到内存中,这会导致内存溢出。

Q3: 文件操作的错误处理最佳实践是什么?

A: 始终使用try-catch包围文件操作,检查特定的错误代码(如ENOENT、EACCES),实现重试机制,并提供有意义的错误信息。

Q4: 如何确保文件写入的原子性?

A: 使用临时文件写入然后重命名的方式实现原子操作,或者使用文件锁机制防止并发写入冲突。

Q5: Node.js文件操作的性能优化技巧有哪些?

A: 使用异步操作、合理设置缓冲区大小、批量处理文件操作、使用Stream处理大文件、避免频繁的小文件操作。


🛠️ 文件系统最佳实践

错误处理模式

javascript
// 推荐的文件操作错误处理模式
async function robustFileOperation(filename) {
    try {
        const data = await fsPromises.readFile(filename, 'utf8');
        return data;
    } catch (error) {
        switch (error.code) {
            case 'ENOENT':
                throw new Error(`文件不存在: ${filename}`);
            case 'EACCES':
                throw new Error(`权限不足: ${filename}`);
            case 'EISDIR':
                throw new Error(`路径是目录而非文件: ${filename}`);
            default:
                throw new Error(`文件操作失败: ${error.message}`);
        }
    }
}

文件操作工具类

javascript
// 通用文件操作工具类
class FileUtils {
    static async ensureDir(dirPath) {
        try {
            await fsPromises.mkdir(dirPath, { recursive: true });
        } catch (error) {
            if (error.code !== 'EEXIST') {
                throw error;
            }
        }
    }
    
    static async copyFile(src, dest) {
        await this.ensureDir(path.dirname(dest));
        await fsPromises.copyFile(src, dest);
    }
    
    static async moveFile(src, dest) {
        await this.copyFile(src, dest);
        await fsPromises.unlink(src);
    }
}

"掌握Node.js文件系统操作是后端开发的基础技能。从简单的文件读写到复杂的流处理,每种技术都有其适用场景。理解同步异步操作的权衡,选择合适的文件处理方式,能够让你的Node.js应用更加高效和稳定。接下来,让我们学习目录操作和文件流处理的高级技巧!"