1. 为什么需要文件监控系统在日常开发中经常会遇到需要监控文件系统变更的场景。比如你可能需要知道某个重要配置文件何时被修改或者需要实时同步某个文件夹中的新增文件。我曾经接手过一个项目客户要求每当上传目录出现新的图片文件时系统要立即生成缩略图。如果每次都轮询检查文件系统不仅效率低下还会造成不必要的资源浪费。这时候FileSystemWatcher就派上用场了。它是.NET框架提供的一个轻量级组件专门用于监控文件系统的变更。相比自己实现轮询机制FileSystemWatcher采用事件驱动的方式只在文件真正发生变化时才触发通知大大提高了效率。2. FileSystemWatcher基础使用2.1 初始化与基本配置创建一个基本的文件监控系统非常简单。首先实例化FileSystemWatcher类然后设置几个关键属性FileSystemWatcher watcher new FileSystemWatcher(); watcher.Path C:\监控目录; // 设置监控路径 watcher.IncludeSubdirectories true; // 是否包含子目录 watcher.Filter *.txt; // 只监控txt文件 watcher.NotifyFilter NotifyFilters.LastWrite | NotifyFilters.FileName; // 监控哪些变更 watcher.EnableRaisingEvents true; // 启用监控这里有几个容易踩坑的地方路径要用双引号括起来如果不设置Filter属性默认会监控所有文件类型NotifyFilter决定了监控哪些类型的变更合理设置可以提高性能2.2 事件注册与处理FileSystemWatcher提供了几个关键事件watcher.Created OnFileCreated; watcher.Changed OnFileChanged; watcher.Renamed OnFileRenamed; watcher.Deleted OnFileDeleted; watcher.Error OnError;事件处理方法示例private static void OnFileChanged(object sender, FileSystemEventArgs e) { Console.WriteLine($文件{e.FullPath}被修改变更类型{e.ChangeType}); }3. 构建高可靠性监控系统3.1 多线程安全处理FileSystemWatcher的事件是在后台线程触发的这意味着如果你要在事件处理中更新UI必须考虑线程安全问题。在WinForms中可以使用Control.Invoke方法private void OnFileCreated(object sender, FileSystemEventArgs e) { if (listView.InvokeRequired) { listView.Invoke(new Action(() OnFileCreated(sender, e))); return; } // 安全更新UI的代码 }3.2 异常处理与重试机制文件系统操作可能会遇到各种异常比如文件被占用、权限不足等。一个好的监控系统应该能处理这些异常private void OnFileChanged(object sender, FileSystemEventArgs e) { int retryCount 0; while (retryCount 3) { try { // 尝试读取文件内容 var content File.ReadAllText(e.FullPath); break; } catch (IOException ex) { retryCount; if (retryCount 3) { LogError($无法读取文件{e.FullPath}: {ex.Message}); } else { Thread.Sleep(500); // 等待500毫秒后重试 } } } }4. 日志记录与持久化4.1 数据库存储方案将监控记录存入数据库可以方便后续查询和分析。这里以SQL Server为例private void LogToDatabase(FileSystemEventArgs e) { string connStr 你的连接字符串; using (SqlConnection conn new SqlConnection(connStr)) { string sql INSERT INTO FileLogs (EventTime, EventType, FileName, FilePath) VALUES (time, type, name, path); SqlCommand cmd new SqlCommand(sql, conn); cmd.Parameters.AddWithValue(time, DateTime.Now); cmd.Parameters.AddWithValue(type, e.ChangeType.ToString()); cmd.Parameters.AddWithValue(name, e.Name); cmd.Parameters.AddWithValue(path, e.FullPath); conn.Open(); cmd.ExecuteNonQuery(); } }4.2 文件日志方案如果不想依赖数据库也可以使用日志文件private void LogToFile(FileSystemEventArgs e) { string logPath C:\logs\file_watcher.log; string logMessage ${DateTime.Now:yyyy-MM-dd HH:mm:ss} - {e.ChangeType} - {e.FullPath}; // 使用File.AppendAllText可以自动处理文件不存在的情况 File.AppendAllText(logPath, logMessage Environment.NewLine); }5. 高级应用场景5.1 批量文件处理优化当监控的目录中短时间内出现大量文件变更时简单的逐个处理可能会导致性能问题。这时候可以考虑使用缓冲队列private readonly ConcurrentQueueFileSystemEventArgs _fileQueue new ConcurrentQueueFileSystemEventArgs(); private readonly System.Timers.Timer _processTimer new System.Timers.Timer(1000); // 1秒处理一次 public FileWatcherService() { _processTimer.Elapsed ProcessQueue; _processTimer.AutoReset true; _processTimer.Start(); } private void OnFileChanged(object sender, FileSystemEventArgs e) { _fileQueue.Enqueue(e); } private void ProcessQueue(object sender, System.Timers.ElapsedEventArgs e) { ListFileSystemEventArgs batch new ListFileSystemEventArgs(); while (_fileQueue.TryDequeue(out var fileEvent)) { batch.Add(fileEvent); if (batch.Count 100) break; // 每次最多处理100个文件 } if (batch.Count 0) { // 批量处理文件 ProcessBatch(batch); } }5.2 监控系统资源占用长时间运行的监控服务需要注意内存和资源管理// 定期检查并重启监控 private System.Timers.Timer _healthTimer new System.Timers.Timer(3600000); // 每小时检查一次 private void StartHealthCheck() { _healthTimer.Elapsed (s, e) { if (_watcher ! null) { _watcher.EnableRaisingEvents false; _watcher.Dispose(); } InitializeWatcher(); // 重新初始化 }; _healthTimer.Start(); }6. 实际项目中的经验分享在真实项目中我发现FileSystemWatcher有几个需要注意的地方首先Changed事件可能会被多次触发。这是因为文件修改可能涉及多个系统操作。解决方案是使用一个简单的防抖机制private readonly Dictionarystring, DateTime _lastEventTimes new Dictionarystring, DateTime(); private void OnFileChanged(object sender, FileSystemEventArgs e) { if (_lastEventTimes.TryGetValue(e.FullPath, out var lastTime)) { if ((DateTime.Now - lastTime).TotalMilliseconds 500) // 500毫秒内忽略重复事件 return; } _lastEventTimes[e.FullPath] DateTime.Now; // 处理文件变更 }其次监控网络共享文件夹时可能会遇到延迟问题。这种情况下适当增加InternalBufferSize可能会有帮助_watcher.InternalBufferSize 65536; // 64KB缓冲区最后记得在应用程序退出时正确释放资源_watcher.EnableRaisingEvents false; _watcher.Dispose();