FileSystemWatcher事件多次触发的解决方法

2024-01-29 03:58

本文主要是介绍FileSystemWatcher事件多次触发的解决方法,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

  public   void  Initial()
 
{
   System.IO.FileSystemWatcher fsw 
= new System.IO.FileSystemWatcher();            
   fsw.Filter 
= "*.*";
   fsw.NotifyFilter 
= NotifyFilters.FileName  | 
                      NotifyFilters.LastWrite 
| 
                      NotifyFilters.CreationTime;

   
// Add event handlers.
   fsw.Created += new FileSystemEventHandler(fsw_Changed);
   fsw.Changed 
+= new FileSystemEventHandler(fsw_Changed);

   
// Begin watching.
   fsw.EnableRaisingEvents = true;
 }


 
void  fsw_Changed( object  sender, FileSystemEventArgs e)
 
{
    MessageBox.Show(
"Changed", e.Name);
 }

 

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

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

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

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

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

       fsw.Filter  =   " *.* " ;
       fsw.NotifyFilter 
=  NotifyFilters.FileName   |  
                          NotifyFilters.LastWrite 
|  
                          NotifyFilters.CreationTime;

       
//  Add event handlers.
      fsw.Created  +=   new  FileSystemEventHandler(fsw_Changed);
      fsw.Changed 
+=   new  FileSystemEventHandler(fsw_Changed);

      
//  Begin watching.
      fsw.EnableRaisingEvents  =   true ;

      
//  Create the timer that will be used to deliver events. Set as disabled
       if  (m_timer  ==   null )
      
{
         
//设置定时器的回调函数。此时定时器未启动
         m_timer = new System.Threading.Timer(new TimerCallback(OnWatchedFileChange), 
                                      
null, Timeout.Infinite, Timeout.Infinite);
      }

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

         void  fsw_Changed( object  sender, FileSystemEventArgs e)
        
{
            Mutex mutex 
= new Mutex(false"FSW");
            mutex.WaitOne();
            
if (!files.Contains(e.Name))
            
{
                files.Add(e.Name);
            }

            mutex.ReleaseMutex();
  
            
//重新设置定时器的触发间隔,并且仅仅触发一次
            m_timer.Change(TimeoutMillis, Timeout.Infinite);
        }

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

         private   void  OnWatchedFileChange( object  state)
        
{
            List
<String> backup = new List<string>();

            Mutex mutex 
= new Mutex(false"FSW");
            mutex.WaitOne();
            backup.AddRange(files);
            files.Clear();
            mutex.ReleaseMutex();

            
            
foreach (string file in backup)
            
{
                MessageBox.Show(
"File Change", file + " changed");
            }

        
        }

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

     public   class  WatcherTimer
    
{
        
private int TimeoutMillis = 2000;

        System.IO.FileSystemWatcher fsw 
= new System.IO.FileSystemWatcher();
        System.Threading.Timer m_timer 
= null;
        List
<String> files = new List<string>();
        FileSystemEventHandler fswHandler 
= null;

        
public WatcherTimer(FileSystemEventHandler watchHandler)
        
{
            m_timer 
= new System.Threading.Timer(new TimerCallback(OnTimer), 
                         
null, Timeout.Infinite, Timeout.Infinite);
            fswHandler 
= watchHandler;

        }



        
public WatcherTimer(FileSystemEventHandler watchHandler, int timerInterval)
        
{
            m_timer 
= new System.Threading.Timer(new TimerCallback(OnTimer), 
                        
null, Timeout.Infinite, Timeout.Infinite);
            TimeoutMillis 
= timerInterval;
            fswHandler 
= watchHandler;

        }


        
public void OnFileChanged(object sender, FileSystemEventArgs e)
        
{
            Mutex mutex 
= new Mutex(false"FSW");
            mutex.WaitOne();
            
if (!files.Contains(e.Name))
            
{
                files.Add(e.Name);
            }

            mutex.ReleaseMutex();

            m_timer.Change(TimeoutMillis, Timeout.Infinite);
        }


        
private void OnTimer(object state)
        
{
            List
<String> backup = new List<string>();

            Mutex mutex 
= new Mutex(false"FSW");
            mutex.WaitOne();
            backup.AddRange(files);
            files.Clear();
            mutex.ReleaseMutex();


            
foreach (string file in backup)
            
{
                fswHandler(
thisnew FileSystemEventArgs(
                       WatcherChangeTypes.Changed, 
string.Empty, file));
            }


        }




}

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

   watcher  =   new  WatcherTimer(fsw_Changed, TimeoutMillis);

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

void  fsw_Changed( object  sender, FileSystemEventArgs e)
{
   
//Read file.
   
//Remove file from folder after reading
      
}

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

fsw.Created  +=   new  FileSystemEventHandler(watcher.OnFileChanged);
fsw.Changed 
+=   new  FileSystemEventHandler(watcher.OnFileChanged);
fsw.Renamed 
+=   new  RenamedEventHandler(watcher.OnFileChanged);
fsw.Deleted 
+=   new  FileSystemEventHandler(watcher.OnFileChanged);

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

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

这篇关于FileSystemWatcher事件多次触发的解决方法的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/655662

相关文章

Java实现任务管理器性能网络监控数据的方法详解

《Java实现任务管理器性能网络监控数据的方法详解》在现代操作系统中,任务管理器是一个非常重要的工具,用于监控和管理计算机的运行状态,包括CPU使用率、内存占用等,对于开发者和系统管理员来说,了解这些... 目录引言一、背景知识二、准备工作1. Maven依赖2. Gradle依赖三、代码实现四、代码详解五

oracle数据库索引失效的问题及解决

《oracle数据库索引失效的问题及解决》本文总结了在Oracle数据库中索引失效的一些常见场景,包括使用isnull、isnotnull、!=、、、函数处理、like前置%查询以及范围索引和等值索引... 目录oracle数据库索引失效问题场景环境索引失效情况及验证结论一结论二结论三结论四结论五总结ora

JAVA中整型数组、字符串数组、整型数和字符串 的创建与转换的方法

《JAVA中整型数组、字符串数组、整型数和字符串的创建与转换的方法》本文介绍了Java中字符串、字符数组和整型数组的创建方法,以及它们之间的转换方法,还详细讲解了字符串中的一些常用方法,如index... 目录一、字符串、字符数组和整型数组的创建1、字符串的创建方法1.1 通过引用字符数组来创建字符串1.2

element-ui下拉输入框+resetFields无法回显的问题解决

《element-ui下拉输入框+resetFields无法回显的问题解决》本文主要介绍了在使用ElementUI的下拉输入框时,点击重置按钮后输入框无法回显数据的问题,具有一定的参考价值,感兴趣的... 目录描述原因问题重现解决方案方法一方法二总结描述第一次进入页面,不做任何操作,点击重置按钮,再进行下

Java调用Python代码的几种方法小结

《Java调用Python代码的几种方法小结》Python语言有丰富的系统管理、数据处理、统计类软件包,因此从java应用中调用Python代码的需求很常见、实用,本文介绍几种方法从java调用Pyt... 目录引言Java core使用ProcessBuilder使用Java脚本引擎总结引言python

Apache Tomcat服务器版本号隐藏的几种方法

《ApacheTomcat服务器版本号隐藏的几种方法》本文主要介绍了ApacheTomcat服务器版本号隐藏的几种方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需... 目录1. 隐藏HTTP响应头中的Server信息编辑 server.XML 文件2. 修China编程改错误

解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题

《解决mybatis-plus-boot-starter与mybatis-spring-boot-starter的错误问题》本文主要讲述了在使用MyBatis和MyBatis-Plus时遇到的绑定异常... 目录myBATis-plus-boot-starpythonter与mybatis-spring-b

Java中switch-case结构的使用方法举例详解

《Java中switch-case结构的使用方法举例详解》:本文主要介绍Java中switch-case结构使用的相关资料,switch-case结构是Java中处理多个分支条件的一种有效方式,它... 目录前言一、switch-case结构的基本语法二、使用示例三、注意事项四、总结前言对于Java初学者

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结