添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
一个更好的文件监控类,基于 DotNet 官方提供的 FileSystemWatcher

一个更好的文件监控类,基于 DotNet 官方提供的 FileSystemWatcher

缘起

前一段时间,想使用 .net 监听特定文件夹中的文件是否发生变化。网上一搜,可以使用 .net 官方提供的 FileSystemWatcher ,很开心的写好了代码。随着使用的不断深入,发现了 FileSystemWatcher 几个不够完善的地方。比如,

  1. 事件触发时,文件可能还不能被访问。
  2. 如果监听选项设置的过多,则有可能会触发多次文件变化事件。
  3. 监听过滤器不够灵活,我没找到同时监听多种特定文件类型的方法。比如,同时只监听 .docx .bmp 文件。

鉴于此,基于 .net 官方提供的 FileSystemWatcher ,我又封装了一个新的类。可以在一定程度上解决以上几个问题。

问题及解决方案

  1. 当事件触发的时候,文件还不能被访问。如何重现?
    重现方法:
    通过共享文件夹拷贝一个大文件,基本上可以重现。
    解决方案:
    在调用用户的回调函数前,先判断文件是否可以访问。
    实现代码:
    WaitForFileReadyToAccess 可以用来开启/关闭此功能。
    WaitUntilCanAccess() 不断尝试访问文件,以此来确定是否可以访问该文件。
    用户可以通过 FileAccessCheckIntervalMs 来设置尝试间隔,单位是毫秒。
    用户可以通过 MaxWaitMs 设置最大等待时间,单位是毫秒。
  2. 相同的事件触发多次。如何重现?
    重现方法:
    NotifyFilters 设置成下面的样子,拷贝一张文件到监听路径下即可重现。
    NotifyFilters _notifyFilters = NotifyFilters.DirectoryName | NotifyFilters.FileName
    | NotifyFilters.LastWrite | NotifyFilters.CreationTime | NotifyFilters.LastAccess
    | NotifyFilters.Attributes | NotifyFilters.Size | NotifyFilters.Security
    ;

    解决方案:
    在调用用户的回调函数前,稍微等一段时间,合并这段时间内的相同事件。
    实现代码:
    TryMergeSameEvent 用来开启/关闭事件合并功能。
    如果 TryMergeSameEvent 为真,那么会通过 eventDataList = eventDataList.Distinct().ToList(); 来去重。
    用户可以通过 DelayTriggerMs 指定延时触发间隔,单位是毫秒。只有在合并事件开启的时候才生效。
  3. 不能同时监听多种特定类型。
    重现方法:
    我没能找到同时监听多种特定类型的方法。
    解决方案:
    封装一个新类。用户可以通过 | 分割多个过滤条件。根据每个过滤条件构造对应的由系统提供的 FileSystemWatcher ,保存到
    List<FileSystemWatcher> 中。
    实现代码:
    char [] splitter = { '|' };
    var filterList = Filters.Split(splitter).ToList();
    foreach ( var filter in filterList)
    {
    FileSystemWatcher watcher = new FileSystemWatcher();

    watcher.Filter = filter;
    watcher.Path = this .Path;
    watcher.IncludeSubdirectories = this .Recursive;
    watcher.NotifyFilter = this .NotifyFilters;
    watcher.Created += this .OnFileSystemEventHandler;
    watcher.Deleted += this .OnFileSystemEventHandler;
    watcher.Changed += this .OnFileSystemEventHandler;
    watcher.Renamed += this .OnRenamedEventHandler;
    watcher.Error += this .OnErrorHandler;
    watcher.EnableRaisingEvents = true;
    WatcherList.Add(watcher);
    }

使用示例

public void OnFileChanged(object sender, System.IO.FileSystemEventArgs e)
  if (e.ChangeType == System.IO.WatcherChangeTypes.Created 
      || e.ChangeType == System.IO.WatcherChangeTypes.Changed)
    this.Invoke(new MethodInvoker(delegate()
        using (var imageStream = new FileStream(e.FullPath, FileMode.Open))
          this.pictureBox1.Image = (Bitmap)Image.FromStream(imageStream);
      catch (Exception ex)
        System.Diagnostics.Debug.WriteLine(string.Format("!!!! {0}", ex.ToString()));
private void Form1_Load(object sender, EventArgs e)