![](/style/images/good.png)
![](/style/images/bad.png)
一个更好的文件监控类,基于 DotNet 官方提供的 FileSystemWatcher
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
几个不够完善的地方。比如,
- 事件触发时,文件可能还不能被访问。
- 如果监听选项设置的过多,则有可能会触发多次文件变化事件。
- 监听过滤器不够灵活,我没找到同时监听多种特定文件类型的方法。比如,同时只监听
.docx
和.bmp
文件。
鉴于此,基于 .net
官方提供的 FileSystemWatcher
,我又封装了一个新的类。可以在一定程度上解决以上几个问题。
问题及解决方案
当事件触发的时候,文件还不能被访问。如何重现?
重现方法:
通过共享文件夹拷贝一个大文件,基本上可以重现。
解决方案:
在调用用户的回调函数前,先判断文件是否可以访问。
实现代码:
WaitForFileReadyToAccess
可以用来开启/关闭此功能。WaitUntilCanAccess()
不断尝试访问文件,以此来确定是否可以访问该文件。用户可以通过
FileAccessCheckIntervalMs
来设置尝试间隔,单位是毫秒。用户可以通过
MaxWaitMs
设置最大等待时间,单位是毫秒。
相同的事件触发多次。如何重现?
重现方法:
当
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
指定延时触发间隔,单位是毫秒。只有在合并事件开启的时候才生效。
不能同时监听多种特定类型。
重现方法:
我没能找到同时监听多种特定类型的方法。
解决方案:
封装一个新类。用户可以通过
|
分割多个过滤条件。根据每个过滤条件构造对应的由系统提供的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
。
github
:https://github.com/BianChengNan/FileSystemWatcherEx
gitee
: https://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
。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK