FileSystemWatcher相关介绍请看MSDN(http://msdn.microsoft.com/zh-cn/library/system.io.filesystemwatcher.aspx),本文只是对Changed事件多次触发做一点处理(纯粹个人见解)。
MSDN对Changed事件的定义为:当对所监视的目录中的文件或目录的大小、系统特性、上次写入时间、上次访问时间或安全权限进行更改时,将引发 Changed 事件。
首先定义一个基础类型:
public class FileWatcher { FileSystemWatcher fsw; public event Action<FileSystemEventArgs> OnChange; public FileWatcher(string path, string filter) { fsw = new FileSystemWatcher(); fsw.Path = path; fsw.Filter = filter; fsw.NotifyFilter = NotifyFilters.LastWrite; fsw.Changed += fsw_Changed; fsw.EnableRaisingEvents = true; } void fsw_Changed(object sender, FileSystemEventArgs e) { if (CanRaiseChange(e)) RaiseChange(e); } protected virtual bool CanRaiseChange(FileSystemEventArgs e) { return true; } protected virtual void RaiseChange(FileSystemEventArgs e) { if (OnChange != null) OnChange.BeginInvoke(e, null, null);//考虑到OnChange执行时间有可能比较长 } }
因为只需要监视文件内容更改,仅仅监视文件最后写入时间已经足够。在本文之前,找了很多网站,均是以System.Threading.Timer来解决多次触发问题,但由于无法对Timer.Change方法传递额外的参数,在准确性上未免打一些折扣,而且某些解决方案无法处理同一时间修改的多个文件,遂决定自己实现。
具体思路为:
1、首先创建一个字典,以文件的完整路径为Key,最后触发Changed时间为Value
2、对于每个触发的Changed事件,首先判断被修改文件的完整路径是否在字典中,如果不存在则向字典添加
3、如果存在于字典中,则判断当前被修改文件上一次Changed事件触发的时间与当前触发时间间隔,这里暂且设置为500毫秒
4、对于500毫秒以内的不作处理
5、对于超出500毫秒的进行处理,并更新当前被修改文件的Changed事件触发时间
代码如下:
public class MyFileWatcher : FileWatcher { private Dictionary<string, DateTime> changesHistory = new Dictionary<string, DateTime>(); public MyFileWatcher(string path, string filter) : base(path, filter) { } protected override bool CanRaiseChange(FileSystemEventArgs e) { if (!changesHistory.ContainsKey(e.FullPath)) { Console.WriteLine("a"); changesHistory.Add(e.FullPath, DateTime.Now); return true; } DateTime now = DateTime.Now; bool canRaise = (now - changesHistory[e.FullPath]).TotalMilliseconds >= 500; if (canRaise) changesHistory[e.FullPath] = now; Console.WriteLine(canRaise); return canRaise; } }
测试代码:
public static void Main(string[] args) { MyFileWatcher mfw = new MyFileWatcher("g:\\test\\", "*.txt"); mfw.OnChange += new Action<FileSystemEventArgs>(fw_OnChange); Console.Read(); } static void fw_OnChange(FileSystemEventArgs obj) { Console.WriteLine("{0} {1}", DateTime.Now, obj.FullPath); }
本方法存在一个问题,就是需要监视的文件增多以后,changesHistory字典会不断增大,对性能会有一定影响。可以考虑在CanRaiseChange方法里删除一部分时间间隔比较长的元素。
另外,对于多线程同时修改一个文件的情况,本方法也无法解决,但已经能满足我的需求。