ZetCode

C# FileSystemWatcher

最后修改于 2025 年 4 月 20 日

本教程探讨了 C# FileSystemWatcher 类,用于监视文件系统更改。 它涵盖了各种场景,从基本事件处理到高级技术(如防抖动和网络监视),并提供了实际示例。

FileSystemWatcher 类是 System.IO 命名空间的一部分,它侦听文件系统事件,例如文件创建、修改、删除或重命名。 它具有高度可配置性,允许开发人员过滤事件并监视特定目录或文件类型。

FileSystemWatcher 非常适合实时应用程序,如自动备份、文件同步工具或日志文件监视器。 本教程通过突出显示不同配置和事件处理策略的示例来演示其用法。

基本 FileSystemWatcher 示例

此示例设置一个基本的 FileSystemWatcher 来监视目录中文本文件的更改。 它处理创建、修改、删除和重命名事件,显示该类的核心功能。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\Temp";
        
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = path;
        watcher.NotifyFilter = NotifyFilters.LastWrite 
                             | NotifyFilters.FileName 
                             | NotifyFilters.DirectoryName;

        watcher.Filter = "*.txt";
        watcher.IncludeSubdirectories = true;

        watcher.Created += OnFileChanged;
        watcher.Changed += OnFileChanged;
        watcher.Deleted += OnFileChanged;
        watcher.Renamed += OnFileRenamed;

        watcher.EnableRaisingEvents = true;

        Console.WriteLine("Monitoring " + path + " for changes...");
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    private static void OnFileChanged(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"{e.ChangeType}: {e.Name}");
    }

    private static void OnFileRenamed(object sender, RenamedEventArgs e)
    {
        Console.WriteLine($"Renamed: {e.OldName} to {e.Name}");
    }
}

FileSystemWatcher 监视 C:\Temp 目录中 .txt 文件的更改,包括子目录。 NotifyFilter 属性指定哪些更改触发事件,例如上次写入时间或文件名更改。 事件处理程序附加到控制台以记录创建、修改、删除和重命名事件。

将 EnableRaisingEvents 设置为 true 将启动监视过程。 程序运行直到按下某个键,并持续显示文件系统事件。 此示例非常适合理解 FileSystemWatcher 的基本设置和事件处理。

监视特定更改

此示例演示如何配置 FileSystemWatcher 以监视特定类型的更改,例如文件属性或大小,使用 NotifyFilter 属性进行精确控制。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\Temp";
        
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = path;
        
        watcher.NotifyFilter = NotifyFilters.Attributes
                             | NotifyFilters.CreationTime
                             | NotifyFilters.Security
                             | NotifyFilters.Size;

        watcher.Changed += OnFileChanged;
        watcher.EnableRaisingEvents = true;

        Console.WriteLine("Monitoring specific changes in " + path);
        Console.WriteLine("Press anyatherapy key to exit.");
        Console.ReadKey();
    }

    private static void OnFileChanged(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"Change detected: {e.ChangeType} on {e.Name}");
        Console.WriteLine($"Full path: {e.FullPath}");
    }
}

NotifyFilter 设置为监视文件属性、创建时间、安全设置和大小的更改,忽略其他事件(如上次写入时间)。 这减少了触发的事件数量,提高了仅特定更改重要的场景中的性能。

事件处理程序记录更改的类型和受影响文件的完整路径。 这种方法对于需要对特定文件系统更改做出反应的应用程序很有用,例如检测文件权限的更改或跟踪新创建的文件。

处理多个事件

此示例展示了如何使用单个处理程序处理多个文件系统事件(用于创建、修改和删除),并使用单独的处理程序处理重命名和错误。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\Temp";
        
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = path;
        
        watcher.Created += OnFileEvent;
        watcher.Changed += OnFileEvent;
        watcher.Deleted += OnFileEvent;
        watcher.Renamed += OnFileRenamedEvent;
        watcher.Error += OnError;

        watcher.EnableRaisingEvents = true;

        Console.WriteLine("Monitoring all file events in " + path);
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    private static void OnFileEvent(object sender, FileSystemEventArgs e)
    {
        switch (e.ChangeType)
        {
            case WatcherChangeTypes.Created:
                Console.WriteLine($"New file created: {e.Name}");
                break;
            case WatcherChangeTypes.Changed:
                Console.WriteLine($"File changed: {e.Name}");
                break;
            case WatcherChangeTypes.Deleted:
                Console.WriteLine($"File deleted: {e.Name}");
                break;
        }
    }

    private static void OnFileRenamedEvent(object sender, RenamedEventArgs e)
    {
        Console.WriteLine($"File renamed from {e.OldName} to {e.Name}");
    }

    private static void OnError(object sender, ErrorEventArgs e)
    {
        Console.WriteLine($"Error: {e.GetException().Message}");
    }
}

单个 OnFileEvent 处理程序使用 switch 语句来区分创建、修改和删除事件。 由于其独特的 RenamedEventArgs 参数(包括旧文件名和新文件名),Renamed 事件使用单独的处理程序。

错误处理程序捕获异常(如权限问题或缓冲区溢出),确保强大的监视。 此设置非常适合需要详细事件跟踪和错误处理的应用程序,为每种类型的文件系统事件提供清晰的反馈。

缓冲和高容量更改

此示例演示了通过增加内部缓冲区大小和实施缓冲区溢出的错误处理来处理大量文件系统更改。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\Temp";
        
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = path;
        
        watcher.InternalBufferSize = 65536; // 64KB
        
        watcher.NotifyFilter = NotifyFilters.LastWrite 
                             | NotifyFilters.FileName 
                             | NotifyFilters.DirectoryName;

        watcher.Created += OnFileChanged;
        watcher.Changed += OnFileChanged;
        watcher.Deleted += OnFileChanged;
        watcher.Error += OnError;

        watcher.EnableRaisingEvents = true;

        Console.WriteLine("Monitoring with large buffer in " + path);
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    private static void OnFileChanged(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"Change: {e.ChangeType} - {e.Name}");
    }

    private static void OnError(object sender, ErrorEventArgs e)
    {
        Console.WriteLine($"Error: {e.GetException().Message}");
    }
}

InternalBufferSize 设置为 64KB(从默认的 8KB),以容纳更多事件,从而降低在高容量场景中发生缓冲区溢出的风险。 NotifyFilter 包括上次写入、文件名和目录名更改,以关注常见事件。

错误处理程序捕获缓冲区溢出或其他问题,并将它们记录到控制台。 事件处理程序保持轻量级以快速处理事件,确保缓冲区可以处理快速更改。 这种方法对于监视具有频繁文件操作的繁忙目录至关重要。

过滤特定文件类型

此示例说明了使用 Filter 和编程检查相结合的高级过滤,以仅监视特定文件类型,例如 .csv 和 .json 文件。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string path = @"C:\Temp";
        
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = path;
        
        watcher.Filter = "*.*";
        watcher.NotifyFilter = NotifyFilters.FileName;
        
        watcher.Created += OnSpecificFileCreated;
        watcher.EnableRaisingEvents = true;

        Console.WriteLine("Watching for new .csv and .json files in " + path);
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    private static void OnSpecificFileCreated(object sender, FileSystemEventArgs e)
    {
        string ext = Path.GetExtension(e.Name).ToLower();
        
        if (ext == ".csv" || ext == ".json")
        {
            Console.WriteLine($"New data file created: {e.Name}");
        }
    }
}

Filter 设置为 *.* 以捕获所有文件,但事件处理程序通过检查文件扩展名来过滤 .csv 和 .json 文件。 NotifyFilter 仅限于文件名更改,从而减少了不必要的事件并提高了效率。

这种两阶段过滤方法非常灵活,允许超出 Filter 属性功能的复杂条件。 它对于需要处理特定文件类型的应用程序很有用,例如监视新数据文件的数据处理工具。

监视网络驱动器

此示例展示了如何使用 FileSystemWatcher 监视网络驱动器,包括针对网络特定问题(如连接丢失)的可靠错误处理。

Program.cs
using System;
using System.IO;

class Program
{
    static void Main()
    {
        string networkPath = @"\\server\share\folder";
        
        try
        {
            FileSystemWatcher watcher = new FileSystemWatcher();
            watcher.Path = networkPath;
            
            watcher.NotifyFilter = NotifyFilters.FileName 
                                 | NotifyFilters.DirectoryName;
            
            watcher.Created += OnNetworkFileChange;
            watcher.Deleted += OnNetworkFileChange;
            watcher.Error += OnNetworkError;
            
            watcher.EnableRaisingEvents = true;

            Console.WriteLine("Monitoring network folder: " + networkPath);
            Console.WriteLine("Press any key to exit.");
            Console.ReadKey();
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Initialization error: {ex.Message}");
        }
    }

    private static void OnNetworkFileChange(object sender, FileSystemEventArgs e)
    {
        Console.WriteLine($"Network change: {e.ChangeType} - {e.Name}");
    }

    private static void OnNetworkError(object sender, ErrorEventArgs e)
    {
        Console.WriteLine($"Network error: {e.GetException().Message}");
    }
}

监视器使用 UNC 符号 (\\server\share\folder) 监视网络路径。 try-catch 块处理初始化错误,例如无效路径或权限问题。 NotifyFilter 侧重于文件和目录名更改,这对于网络监视是可靠的。

错误处理程序记录特定于网络的问题,例如连接断开,使程序在不稳定的网络环境中具有鲁棒性。 此设置适用于在网络驱动器上监视共享文件夹的应用程序,其中连接可能是不稳定的。

高级事件防抖动

此示例实现了事件防抖动,以将多个快速事件处理为单个逻辑更改,从而防止对同一文件修改进行重复处理。

Program.cs
using System;
using System.IO;
using System.Threading;

class Program
{
    private static Timer debounceTimer;
    private static string lastChangedFile;
    private static object lockObj = new object();

    static void Main()
    {
        string path = @"C:\Temp";
        
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = path;
        
        watcher.NotifyFilter = NotifyFilters.LastWrite;
        watcher.Changed += OnFileChangedDebounced;
        watcher.EnableRaisingEvents = true;

        Console.WriteLine("Monitoring with debounce in " + path);
        Console.WriteLine("Press any key to exit.");
        Console.ReadKey();
    }

    private static void OnFileChangedDebounced(object sender, FileSystemEventArgs e)
    {
        lock (lockObj)
        {
            lastChangedFile = e.FullPath;
            
            debounceTimer?.Dispose();
            debounceTimer = new Timer(DebounceCallback, null, 500, Timeout.Infinite);
        }
    }

    private static void DebounceCallback(object state)
    {
        lock (lockObj)
        {
            Console.WriteLine($"File modified (debounced): {lastChangedFile}");
            debounceTimer.Dispose();
            debounceTimer = null;
        }
    }
}

监视器监视上次写入的更改,这可能会为单个文件保存触发多个事件(例如,在文本编辑器自动保存期间)。 使用 Timer 来抖动事件,在处理之前等待最后一个事件之后的 500 毫秒。 lock 确保更新共享变量时的线程安全。

如果在 500 毫秒内发生新事件,则会重置计时器,确保仅处理最终事件。 这种技术对于诸如日志监视器之类的应用程序至关重要,在这些应用程序中,快速的文件更改可能会压垮系统或导致冗余处理。

来源

C# FileSystemWatcher - 参考

在本文中,我们探讨了使用 FileSystemWatcher 在 C# 中进行文件系统监视。

作者

我叫 Jan Bodnar,我是一位热情的程序员,拥有丰富的编程经验。 自 2007 年以来,我一直在撰写编程文章。 迄今为止,我已经撰写了 1,400 多篇文章和 8 本电子书。 我拥有超过十年的编程教学经验。

列出所有 C# 教程