文件系统操作 #

一、fs 模块概述 #

1.1 引入 fs 模块 #

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

1.2 同步与异步 #

javascript
// 同步操作
const data = fs.readFileSync('file.txt', 'utf-8');

// 异步操作(回调)
fs.readFile('file.txt', 'utf-8', (err, data) => {
    if (err) throw err;
    console.log(data);
});

// 异步操作(Promise)
const data = await fsPromises.readFile('file.txt', 'utf-8');

二、文件读写 #

2.1 读取文件 #

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

// 读取文本文件
async function readTextFile(filePath) {
    try {
        const content = await fs.readFile(filePath, 'utf-8');
        return content;
    } catch (error) {
        console.error('读取失败:', error);
        throw error;
    }
}

// 读取 JSON 文件
async function readJsonFile(filePath) {
    const content = await fs.readFile(filePath, 'utf-8');
    return JSON.parse(content);
}

// 读取二进制文件
async function readBinaryFile(filePath) {
    const buffer = await fs.readFile(filePath);
    return buffer;
}

2.2 写入文件 #

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

// 写入文本文件
async function writeTextFile(filePath, content) {
    await fs.writeFile(filePath, content, 'utf-8');
}

// 写入 JSON 文件
async function writeJsonFile(filePath, data) {
    const content = JSON.stringify(data, null, 2);
    await fs.writeFile(filePath, content, 'utf-8');
}

// 写入二进制文件
async function writeBinaryFile(filePath, buffer) {
    await fs.writeFile(filePath, buffer);
}

// 追加内容
async function appendToFile(filePath, content) {
    await fs.appendFile(filePath, content, 'utf-8');
}

2.3 流式读写 #

javascript
const fs = require('fs');

// 读取流
function readLargeFile(filePath, onChunk) {
    return new Promise((resolve, reject) => {
        const stream = fs.createReadStream(filePath, { highWaterMark: 64 * 1024 });
        
        stream.on('data', (chunk) => {
            onChunk(chunk);
        });
        
        stream.on('end', () => {
            resolve();
        });
        
        stream.on('error', (error) => {
            reject(error);
        });
    });
}

// 写入流
function writeLargeFile(filePath, getChunks) {
    return new Promise((resolve, reject) => {
        const stream = fs.createWriteStream(filePath);
        
        const writeChunk = () => {
            const chunk = getChunks();
            if (chunk) {
                stream.write(chunk, writeChunk);
            } else {
                stream.end();
            }
        };
        
        stream.on('finish', resolve);
        stream.on('error', reject);
        
        writeChunk();
    });
}

// 复制文件
function copyFile(source, target) {
    return new Promise((resolve, reject) => {
        const readStream = fs.createReadStream(source);
        const writeStream = fs.createWriteStream(target);
        
        readStream.pipe(writeStream);
        
        writeStream.on('finish', resolve);
        writeStream.on('error', reject);
    });
}

三、目录操作 #

3.1 创建目录 #

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

// 创建目录
await fs.mkdir('new-dir');

// 递归创建目录
await fs.mkdir('path/to/new/dir', { recursive: true });

3.2 读取目录 #

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

// 读取目录内容
const files = await fs.readdir('directory');

// 带详细信息
const files = await fs.readdir('directory', { withFileTypes: true });
files.forEach(file => {
    console.log(file.name);
    console.log(file.isFile());
    console.log(file.isDirectory());
});

3.3 删除目录 #

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

// 删除空目录
await fs.rmdir('empty-dir');

// 递归删除目录
await fs.rm('directory', { recursive: true, force: true });

3.4 遍历目录 #

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

async function walkDirectory(dir, callback) {
    const files = await fs.readdir(dir, { withFileTypes: true });
    
    for (const file of files) {
        const filePath = path.join(dir, file.name);
        
        if (file.isDirectory()) {
            await walkDirectory(filePath, callback);
        } else {
            await callback(filePath);
        }
    }
}

// 使用
await walkDirectory('/path/to/dir', async (filePath) => {
    console.log(filePath);
});

四、文件信息 #

4.1 获取文件状态 #

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

async function getFileInfo(filePath) {
    const stats = await fs.stat(filePath);
    
    return {
        isFile: stats.isFile(),
        isDirectory: stats.isDirectory(),
        size: stats.size,
        created: stats.birthtime,
        modified: stats.mtime,
        accessed: stats.atime
    };
}

4.2 检查文件存在 #

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

async function fileExists(filePath) {
    try {
        await fs.access(filePath);
        return true;
    } catch {
        return false;
    }
}

// 或使用 stat
async function fileExists(filePath) {
    try {
        await fs.stat(filePath);
        return true;
    } catch {
        return false;
    }
}

4.3 获取文件扩展名 #

javascript
const path = require('path');

const ext = path.extname('file.txt');  // '.txt'
const name = path.basename('file.txt', '.txt');  // 'file'
const dir = path.dirname('/path/to/file.txt');  // '/path/to'

五、文件操作 #

5.1 复制文件 #

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

// 复制文件
await fs.copyFile('source.txt', 'target.txt');

// 带标志
await fs.copyFile('source.txt', 'target.txt', fs.constants.COPYFILE_EXCL);

5.2 移动文件 #

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

// 移动/重命名文件
await fs.rename('old-path.txt', 'new-path.txt');

5.3 删除文件 #

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

// 删除文件
await fs.unlink('file.txt');

5.4 截断文件 #

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

// 截断文件到指定长度
await fs.truncate('file.txt', 100);

六、文件权限 #

6.1 修改权限 #

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

// 修改权限
await fs.chmod('file.txt', 0o755);

// 修改所有者
await fs.chown('file.txt', uid, gid);

6.2 检查权限 #

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

// 检查可读
await fs.access('file.txt', constants.R_OK);

// 检查可写
await fs.access('file.txt', constants.W_OK);

// 检查可执行
await fs.access('file.txt', constants.X_OK);

// 检查存在
await fs.access('file.txt', constants.F_OK);

七、文件监听 #

7.1 fs.watch #

javascript
const fs = require('fs');

// 监听文件变化
const watcher = fs.watch('file.txt', (eventType, filename) => {
    console.log('事件类型:', eventType);
    console.log('文件名:', filename);
});

// 关闭监听
watcher.close();

7.2 fs.watchFile #

javascript
const fs = require('fs');

// 轮询监听文件变化
fs.watchFile('file.txt', (curr, prev) => {
    console.log('当前修改时间:', curr.mtime);
    console.log('之前修改时间:', prev.mtime);
});

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

7.3 监听目录 #

javascript
const fs = require('fs');
const path = require('path');

function watchDirectory(dir, callback) {
    const watcher = fs.watch(dir, { recursive: true }, (eventType, filename) => {
        const filePath = path.join(dir, filename);
        callback(eventType, filePath);
    });
    
    return watcher;
}

// 使用
const watcher = watchDirectory('/path/to/dir', (eventType, filePath) => {
    console.log(`${eventType}: ${filePath}`);
});

八、路径处理 #

8.1 path 模块 #

javascript
const path = require('path');

// 路径拼接
const fullPath = path.join('/path', 'to', 'file.txt');
// '/path/to/file.txt'

// 解析路径
const parsed = path.parse('/path/to/file.txt');
// {
//   root: '/',
//   dir: '/path/to',
//   base: 'file.txt',
//   ext: '.txt',
//   name: 'file'
// }

// 格式化路径
const formatted = path.format({
    dir: '/path/to',
    base: 'file.txt'
});

// 规范化路径
const normalized = path.normalize('/path/../to/./file.txt');
// '/to/file.txt'

// 相对路径
const relative = path.relative('/path/to', '/path/from');
// '../from'

// 绝对路径
const absolute = path.resolve('file.txt');
// '/current/working/directory/file.txt'

8.2 跨平台路径 #

javascript
const path = require('path');

// 路径分隔符
console.log(path.sep);  // Windows: '\\', Unix: '/'

// 分隔符
console.log(path.delimiter);  // Windows: ';', Unix: ':'

// 跨平台路径
const dataPath = path.join(app.getPath('userData'), 'data', 'config.json');

九、临时文件 #

9.1 创建临时文件 #

javascript
const fs = require('fs').promises;
const path = require('path');
const os = require('os');

async function createTempFile(content) {
    const tempDir = os.tmpdir();
    const tempFile = path.join(tempDir, `temp-${Date.now()}.txt`);
    
    await fs.writeFile(tempFile, content);
    
    return tempFile;
}

9.2 临时目录 #

javascript
const fs = require('fs').promises;
const path = require('path');
const os = require('os');

async function createTempDir() {
    const tempDir = os.tmpdir();
    const tempPath = path.join(tempDir, `myapp-${Date.now()}`);
    
    await fs.mkdir(tempPath);
    
    return tempPath;
}

十、文件工具类 #

10.1 完整工具类 #

javascript
// fileUtils.js
const fs = require('fs').promises;
const path = require('path');

class FileUtils {
    static async exists(filePath) {
        try {
            await fs.access(filePath);
            return true;
        } catch {
            return false;
        }
    }

    static async readJson(filePath) {
        const content = await fs.readFile(filePath, 'utf-8');
        return JSON.parse(content);
    }

    static async writeJson(filePath, data) {
        const content = JSON.stringify(data, null, 2);
        await fs.writeFile(filePath, content, 'utf-8');
    }

    static async ensureDir(dirPath) {
        if (!(await this.exists(dirPath))) {
            await fs.mkdir(dirPath, { recursive: true });
        }
    }

    static async copyDir(src, dest) {
        await this.ensureDir(dest);
        const entries = await fs.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 this.copyDir(srcPath, destPath);
            } else {
                await fs.copyFile(srcPath, destPath);
            }
        }
    }

    static async removeDir(dirPath) {
        await fs.rm(dirPath, { recursive: true, force: true });
    }

    static async getFiles(dirPath, ext = null) {
        const files = [];
        const entries = await fs.readdir(dirPath, { withFileTypes: true });

        for (const entry of entries) {
            const fullPath = path.join(dirPath, entry.name);
            if (entry.isDirectory()) {
                const subFiles = await this.getFiles(fullPath, ext);
                files.push(...subFiles);
            } else if (!ext || path.extname(fullPath) === ext) {
                files.push(fullPath);
            }
        }

        return files;
    }

    static async getFileSize(filePath) {
        const stats = await fs.stat(filePath);
        return stats.size;
    }

    static formatSize(bytes) {
        const units = ['B', 'KB', 'MB', 'GB', 'TB'];
        let i = 0;
        while (bytes >= 1024 && i < units.length - 1) {
            bytes /= 1024;
            i++;
        }
        return `${bytes.toFixed(2)} ${units[i]}`;
    }
}

module.exports = FileUtils;

10.2 使用工具类 #

javascript
const FileUtils = require('./fileUtils');

// 检查文件存在
if (await FileUtils.exists('config.json')) {
    const config = await FileUtils.readJson('config.json');
}

// 确保目录存在
await FileUtils.ensureDir('/path/to/dir');

// 获取所有 JS 文件
const jsFiles = await FileUtils.getFiles('/path/to/dir', '.js');

// 格式化文件大小
const size = FileUtils.formatSize(1024 * 1024);  // '1.00 MB'

十一、总结 #

11.1 核心要点 #

要点 说明
fs.promises 推荐使用 Promise API
流式读写 大文件使用流处理
路径处理 使用 path 模块跨平台
错误处理 始终处理文件操作错误
文件监听 watch/watchFile 监听变化

11.2 下一步 #

现在你已经掌握了文件系统操作,接下来让我们学习 数据库集成,深入了解 Electron 应用中的数据库使用!

最后更新:2026-03-28