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方法里删除一部分时间间隔比较长的元素。
另外,对于多线程同时修改一个文件的情况,本方法也无法解决,但已经能满足我的需求。