2

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

 3 years ago
source link: https://bianchengnan.gitee.io/articles/A-better-file-system-wacher-based-on-dotnet-FileSystemWatcher/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

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

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

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

问题及解决方案

  1. 当事件触发的时候,文件还不能被访问。如何重现?

    重现方法:

    通过共享文件夹拷贝一个大文件,基本上可以重现。

    解决方案:

    在调用用户的回调函数前,先判断文件是否可以访问。

    实现代码:

    WaitForFileReadyToAccess 可以用来开启/关闭此功能。

    WaitUntilCanAccess() 不断尝试访问文件,以此来确定是否可以访问该文件。

    用户可以通过 FileAccessCheckIntervalMs 来设置尝试间隔,单位是毫秒。

    用户可以通过 MaxWaitMs 设置最大等待时间,单位是毫秒。

  1. 相同的事件触发多次。如何重现?

    重现方法:

    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 指定延时触发间隔,单位是毫秒。只有在合并事件开启的时候才生效。

  1. 不能同时监听多种特定类型。

    重现方法:

    我没能找到同时监听多种特定类型的方法。

    解决方案:

    封装一个新类。用户可以通过 | 分割多个过滤条件。根据每个过滤条件构造对应的由系统提供的 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()
{
try
{
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)
{
var monitorPath = System.AppDomain.CurrentDomain.BaseDirectory;
var fileWatcherEx = new FileSystemWatcherEx(monitorPath, "*.bmp|*.png|*.jpg|*.gif", true, "", OnFileChanged, OnFileChanged, OnFileChanged);
fileWatcherEx.Start();
}

我已经把这个简单的类开源了。欢迎感兴趣的小伙伴儿 clone star pr

githubhttps://github.com/BianChengNan/FileSystemWatcherEx

giteehttps://gitee.com/bianchengnan/FileSystemWatcherEx

如果不想克隆源码,也可以直接下载压缩包:

百度云:https://pan.baidu.com/s/1OBSFpQYRDQHhO5A0Yviqmw 提取码: yic3

CSDN:https://download.csdn.net/download/xiaoyanilw/19648448

犹记得,2013 年在做网盘的时候(用的语言是 C++),也有监听文件变化的需求,我们使用的是 win32 api ReadDirectoryChanges 来实现的。相比原生 api.net 中的 FileSystemWatcher 确实方便了很多。但还有一些不方便的地方。如果你也有类似需求,可以试试 FileSystemWatcherEx


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK