MST
星途 面试题库

面试题:Node.js fs模块自定义文件系统监控机制的实现

假设现有一个需求,需要对特定目录下的文件变化进行监控,如文件的创建、修改和删除。然而Node.js原生的fs.watch有时在某些系统上表现不稳定。请设计并实现一个基于fs模块的自定义文件系统监控机制,要考虑到跨平台兼容性、性能优化以及事件的准确捕获与处理,详细说明设计思路和核心代码实现。
14.1万 热度难度
前端开发Node.js

知识考点

AI 面试

面试题答案

一键面试

设计思路

  1. 跨平台兼容性:通过使用fs.watchfs.watchFile结合的方式,fs.watch是基于操作系统的文件系统事件通知机制,不同系统实现不同,fs.watchFile是轮询方式,在fs.watch不稳定时作为备用。
  2. 性能优化:对于fs.watch,尽量利用其高效的事件驱动特性;对于fs.watchFile,设置合理的轮询间隔,避免过于频繁的文件状态检查。
  3. 事件准确捕获与处理:定义清晰的事件类型(创建、修改、删除),并通过事件发射器(EventEmitter)将事件准确地传递给调用者。

核心代码实现

const fs = require('fs');
const path = require('path');
const EventEmitter = require('events');

class CustomFileSystemMonitor extends EventEmitter {
    constructor(directory) {
        super();
        this.directory = directory;
        this.fileStates = new Map();
        this.initWatchers();
    }

    initWatchers() {
        const watcher = fs.watch(this.directory, { recursive: true }, (eventType, filename) => {
            if (eventType === 'change') {
                const filePath = path.join(this.directory, filename);
                if (fs.statSync(filePath).isFile()) {
                    this.emit('fileChanged', filePath);
                }
            } else if (eventType === 'rename') {
                const oldFilePath = path.join(this.directory, filename);
                if (this.fileStates.has(oldFilePath)) {
                    this.emit('fileDeleted', oldFilePath);
                    this.fileStates.delete(oldFilePath);
                }
            }
        });

        watcher.on('error', (err) => {
            console.error('fs.watch error:', err);
            this.fallbackWatch();
        });
    }

    fallbackWatch() {
        const files = fs.readdirSync(this.directory);
        files.forEach((file) => {
            const filePath = path.join(this.directory, file);
            this.fileStates.set(filePath, fs.statSync(filePath).mtime);
            fs.watchFile(filePath, { persistent: true, interval: 1000 }, (curr, prev) => {
                if (curr.mtime.getTime()!== prev.mtime.getTime()) {
                    this.emit('fileChanged', filePath);
                    this.fileStates.set(filePath, curr.mtime);
                }
            });
        });

        setInterval(() => {
            const newFiles = fs.readdirSync(this.directory);
            newFiles.forEach((file) => {
                const filePath = path.join(this.directory, file);
                if (!this.fileStates.has(filePath)) {
                    this.emit('fileCreated', filePath);
                    this.fileStates.set(filePath, fs.statSync(filePath).mtime);
                }
            });

            const keys = Array.from(this.fileStates.keys());
            keys.forEach((filePath) => {
                if (!newFiles.includes(path.basename(filePath))) {
                    this.emit('fileDeleted', filePath);
                    this.fileStates.delete(filePath);
                }
            });
        }, 1000);
    }
}

// 使用示例
const monitor = new CustomFileSystemMonitor(__dirname);
monitor.on('fileCreated', (filePath) => {
    console.log('File created:', filePath);
});
monitor.on('fileChanged', (filePath) => {
    console.log('File changed:', filePath);
});
monitor.on('fileDeleted', (filePath) => {
    console.log('File deleted:', filePath);
});