1、问题描述
      程序里需要监视某个目录下的文件变化情况: 一旦目录中出现新文件或者旧的文件被覆盖,程序需要读取文件内容并进行处理。于是使用了下面的代码:

FileSystemWatcher事件多次触发的解决方法 public void Initial()

结果发现当一个文件产生变化时,Change事件被反复触发了好几次。这样可能的结果是造成同一文件的重复处理。

2、解决方案:
在Google上进行一番搜索后,得到了下面的一段信息: <<http://www.cnblogs.com/RicCC/archive/2006/12/16/filesystem-watcher.html>>
"...可以参考log4net的做法。通过一个计时器,在文件事件处理中让计时器延迟一段时间之后,再执行加载新的配置文件操作。这样可以避免对文件做一次操作触发了多个更改事件,而多次加载配置文件。"

研究了log4net的代码 - XmlConfigurator.cs,然后参照log4net对代码作了如下改动:
基本思想是使用定时器,在事件触发时开始启动定时器,并记下文件名。当定时器到时,才真正对文件进行处理。
(1). 定义变量

FileSystemWatcher事件多次触发的解决方法private int TimeoutMillis = 2000//定时器触发间隔
FileSystemWatcher事件多次触发的解决方法
System.IO.FileSystemWatcher fsw = new System.IO.FileSystemWatcher();
FileSystemWatcher事件多次触发的解决方法System.Threading.Timer m_timer 
= null;
FileSystemWatcher事件多次触发的解决方法List
<String> files = new List<string>(); //记录待处理文件的队列
FileSystemWatcher事件多次触发的解决方法

(2). 初始化FileSystemWatcher和定时器

FileSystemWatcher事件多次触发的解决方法       fsw.Filter = "*.*";
FileSystemWatcher事件多次触发的解决方法       fsw.NotifyFilter 
= NotifyFilters.FileName  | 
FileSystemWatcher事件多次触发的解决方法                          NotifyFilters.LastWrite 
| 
FileSystemWatcher事件多次触发的解决方法                          NotifyFilters.CreationTime;
FileSystemWatcher事件多次触发的解决方法
FileSystemWatcher事件多次触发的解决方法       
// Add event handlers.
FileSystemWatcher事件多次触发的解决方法
      fsw.Created += new FileSystemEventHandler(fsw_Changed);
FileSystemWatcher事件多次触发的解决方法      fsw.Changed 
+= new FileSystemEventHandler(fsw_Changed);
FileSystemWatcher事件多次触发的解决方法
FileSystemWatcher事件多次触发的解决方法      
// Begin watching.
FileSystemWatcher事件多次触发的解决方法
      fsw.EnableRaisingEvents = true;
FileSystemWatcher事件多次触发的解决方法
FileSystemWatcher事件多次触发的解决方法      
// Create the timer that will be used to deliver events. Set as disabled
FileSystemWatcher事件多次触发的解决方法
      if (m_timer == null)

(3). 文件监视事件触发代码:修改定时器,记录文件名待以后处理

FileSystemWatcher事件多次触发的解决方法        void fsw_Changed(object sender, FileSystemEventArgs e)

(4). 定时器事件触发代码:进行文件的实际处理

FileSystemWatcher事件多次触发的解决方法        private void OnWatchedFileChange(object state)

 将上面的代码整理一下,封装成一个类,使用上更加便利一些:

FileSystemWatcher事件多次触发的解决方法    public class WatcherTimer
}

在主调程序使用非常简单,只需要如下2步:
1、生成用于文件监视的定时器对象

FileSystemWatcher事件多次触发的解决方法   watcher = new WatcherTimer(fsw_Changed, TimeoutMillis);
FileSystemWatcher事件多次触发的解决方法

其中fsw_Changed是你自己的文件监视事件代码,将它传递给定时器对象的目的是用于定时到时的时候定时器对象可以调用你自己真正用于处理文件的代码。例如:

FileSystemWatcher事件多次触发的解决方法void fsw_Changed(object sender, FileSystemEventArgs e)

2、将FileSystemWatcher的Create/Change/Rename/Delete等事件句柄关联到定时器的事件上

FileSystemWatcher事件多次触发的解决方法fsw.Created += new FileSystemEventHandler(watcher.OnFileChanged);
FileSystemWatcher事件多次触发的解决方法fsw.Changed 
+= new FileSystemEventHandler(watcher.OnFileChanged);
FileSystemWatcher事件多次触发的解决方法fsw.Renamed 
+= new RenamedEventHandler(watcher.OnFileChanged);
FileSystemWatcher事件多次触发的解决方法fsw.Deleted 
+= new FileSystemEventHandler(watcher.OnFileChanged);
FileSystemWatcher事件多次触发的解决方法

这一步的目的是当有任何文件监视事件发生时,都能通知到定时器,定时器可以从最后一次发生的事件开始计时,在该计时未到时之前的任何事件都只会重新使计时器计时,而不会真正触发文件监视事件。

要注意的是,采用了以上的代码后,你真正用于处理文件监视事件的代码被调用的时候只有其中的e.Name是有值的。考虑到被监视的文件目录应该已经知道了,所以e.FullPath被赋值为string.Empty并不是不能接受的。

完整的代码请下载示例程序。  
  

相关文章: