最近要用到FileSystemWatcher来监控某个目录中的文件是否发生改变,如果改变就执行相应的操作。但在开发过程中,发现FileSystemWatcher在文件创建或修改后,会触发多个Created或Changed事件,具体原因就是处理文件的过程中执行了多次文件系统操作,触发了多次事件。具体可以参看微软的关于FileSystemWatcher这方面解释Troubleshooting FileSystemWatcher Components另外我在网上发现 Consolidate Multiple FileSystemWatcher Events 关于这方面的解决办法,比较实用,方便快速引入到项目中。

来自MSDN的问题说明

Troubleshooting FileSystemWatcher Components

Visual Studio .NET 2003
 
其他版本
 
此主题尚未评级 - 评价此主题
 

You may encounter the following situations while working with the FileSystemWatcher component:

UNC Path Names Not Accepted on Windows NT 4.0 Computers

If you are working with a FileSystemWatcher component on a Windows NT version 4.0 computer and trying to set its path to monitor file system activity on a different Windows NT version 4.0 computer, you will not be able to specify a UNC-based path value in the Path property to point to the computer in question. You can only set UNC-based values when working on Windows 2000 computers.

Cannot Watch Windows 95 or Windows 98 Directories

If you set your FileSystemWatcher component to reference a directory on a Windows 95 or Windows 98 computer, you will receive an error about an invalid directory path when the project runs. When using FileSystemWatcher, you cannot watch directories on computers running Windows 95 or Windows 98.

Multiple Created Events Generated for a Single Action

You may notice in certain situations that a single creation event generates multiple Created events that are handled by your component. For example, if you use aFileSystemWatcher component to monitor the creation of new files in a directory, and then test it by using Notepad to create a file, you may see two Created events generated even though only a single file was created. This is because Notepad performs multiple file system actions during the writing process. Notepad writes to the disk in batches that create the content of the file and then the file attributes. Other applications may perform in the same manner. Because FileSystemWatcher monitors the operating system activities, all events that these applications fire will be picked up.

Note   Notepad may also cause other interesting event generations. For example, if you use the ChangeEventFilter to specify that you want to watch only for attribute changes, and then you write to a file in the directory you are watching using Notepad, you will raise an event . This is because Notepad updates theArchived attribute for the file during this operation.

Unexpected Events Generated on Directories

Changing a file within a directory you are monitoring with a FileSystemWatcher component generates not only a Changed event on the file but also a similar event for the directory itself. This is because the directory maintains several types of information for each file it contains — the names and sizes of files, their modification dates, attributes, and so on. Whenever one of these attributes changes, a change is associated with the directory as well.

解决方案

The .NET framework provides a FileSystemWatcher class that can be used to monitor the file system for changes. My requirements were to monitor a directory for new files or changes to existing files. When a change occurs, the application needs to read the file and immediately perform some operation based on the contents of the file.

While doing some manual testing of my initial implementation it was very obvious that theFileSystemWatcher was firing multiple events whenever I made a change to a file or copied a file into the directory being monitored. I came across the following in the MSDNdocumentation’s Troubleshooting FileSystemWatcher Components

Multiple Created Events Generated for a Single Action

You may notice in certain situations that a single creation event generates multiple Created events that are handled by your component. For example, if you use a FileSystemWatcher component to monitor the creation of new files in a directory, and then test it by using Notepad to create a file, you may see two Created events generated even though only a single file was created. This is because Notepad performs multiple file system actions during the writing process. Notepad writes to the disk in batches that create the content of the file and then the file attributes. Other applications may perform in the same manner. Because FileSystemWatcher monitors the operating system activities, all events that these applications fire will be picked up.

Note: Notepad may also cause other interesting event generations. For example, if you use the ChangeEventFilter to specify that you want to watch only for attribute changes, and then you write to a file in the directory you are watching using Notepad, you will raise an event. This is because Notepad updates the Archived attribute for the file during this operation.

I did some searching and was surprised that .NET did not provide any kind of wrapper around the FileSystemWatcher to make it a bit more user friendly. I ended up writing my own wrapper that would monitor a directory and only throw one event when a new file was created, or an existing file was changed.

In order to consolidate the multiple FileSystemWatcher events down to a single event, I save the timestamp when each event is received, and I check back every so often (using a Timer) to find paths that have not caused additional events in a while. When one of these paths is ready, a single Changed event is fired. An additional benefit of this technique is that the event from the FileSystemWatcher is handled very quickly, which could help prevent its internal buffer from filling up.

Here is the code for a DirectoryMonitor class that consolidates multiple Win32 events into a single Change event for each change:

解决方案代码

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.IO;
  6. using System.Threading;
  7. namespace ShareReadFile
  8. {
  9. public delegate void FileSystemEvent(String path);
  10. public interface IDirectoryMonitor
  11. {
  12. event FileSystemEvent Change;
  13. void Start();
  14. }
  15. public class DirectoryMonitor : IDirectoryMonitor
  16. {
  17. private readonly FileSystemWatcher m_fileSystemWatcher = new FileSystemWatcher();
  18. private readonly Dictionary<string, DateTime> m_pendingEvents = new Dictionary<string, DateTime>();
  19. private readonly Timer m_timer;
  20. private bool m_timerStarted = false;
  21. public DirectoryMonitor(string dirPath)
  22. {
  23. m_fileSystemWatcher.Path = dirPath;
  24. m_fileSystemWatcher.IncludeSubdirectories = false;
  25. m_fileSystemWatcher.Created += new FileSystemEventHandler(OnChange);
  26. m_fileSystemWatcher.Changed += new FileSystemEventHandler(OnChange);
  27. m_timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
  28. }
  29. public event FileSystemEvent Change;
  30. public void Start()
  31. {
  32. m_fileSystemWatcher.EnableRaisingEvents = true;
  33. }
  34. private void OnChange(object sender, FileSystemEventArgs e)
  35. {
  36. // Don't want other threads messing with the pending events right now
  37. lock (m_pendingEvents)
  38. {
  39. // Save a timestamp for the most recent event for this path
  40. m_pendingEvents[e.FullPath] = DateTime.Now;
  41. // Start a timer if not already started
  42. if (!m_timerStarted)
  43. {
  44. m_timer.Change(100, 100);
  45. m_timerStarted = true;
  46. }
  47. }
  48. }
  49. private void OnTimeout(object state)
  50. {
  51. List<string> paths;
  52. // Don't want other threads messing with the pending events right now
  53. lock (m_pendingEvents)
  54. {
  55. // Get a list of all paths that should have events thrown
  56. paths = FindReadyPaths(m_pendingEvents);
  57. // Remove paths that are going to be used now
  58. paths.ForEach(delegate(string path)
  59. {
  60. m_pendingEvents.Remove(path);
  61. });
  62. // Stop the timer if there are no more events pending
  63. if (m_pendingEvents.Count == 0)
  64. {
  65. m_timer.Change(Timeout.Infinite, Timeout.Infinite);
  66. m_timerStarted = false;
  67. }
  68. }
  69. // Fire an event for each path that has changed
  70. paths.ForEach(delegate(string path)
  71. {
  72. FireEvent(path);
  73. });
  74. }
  75. private List<string> FindReadyPaths(Dictionary<string, DateTime> events)
  76. {
  77. List<string> results = new List<string>();
  78. DateTime now = DateTime.Now;
  79. foreach (KeyValuePair<string, DateTime> entry in events)
  80. {
  81. // If the path has not received a new event in the last 75ms
  82. // an event for the path should be fired
  83. double diff = now.Subtract(entry.Value).TotalMilliseconds;
  84. if (diff >= 75)
  85. {
  86. results.Add(entry.Key);
  87. }
  88. }
  89. return results;
  90. }
  91. private void FireEvent(string path)
  92. {
  93. FileSystemEvent evt = Change;
  94. if (evt != null)
  95. {
  96. evt(path);
  97. }
  98. }
  99. }
  100. }
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading; namespace ShareReadFile
{
public delegate void FileSystemEvent(String path); public interface IDirectoryMonitor
{
event FileSystemEvent Change;
void Start();
} public class DirectoryMonitor : IDirectoryMonitor
{
private readonly FileSystemWatcher m_fileSystemWatcher = new FileSystemWatcher();
private readonly Dictionary<string, DateTime> m_pendingEvents = new Dictionary<string, DateTime>();
private readonly Timer m_timer;
private bool m_timerStarted = false; public DirectoryMonitor(string dirPath)
{
m_fileSystemWatcher.Path = dirPath;
m_fileSystemWatcher.IncludeSubdirectories = false;
m_fileSystemWatcher.Created += new FileSystemEventHandler(OnChange);
m_fileSystemWatcher.Changed += new FileSystemEventHandler(OnChange); m_timer = new Timer(OnTimeout, null, Timeout.Infinite, Timeout.Infinite);
} public event FileSystemEvent Change; public void Start()
{
m_fileSystemWatcher.EnableRaisingEvents = true;
} private void OnChange(object sender, FileSystemEventArgs e)
{
// Don't want other threads messing with the pending events right now
lock (m_pendingEvents)
{
// Save a timestamp for the most recent event for this path
m_pendingEvents[e.FullPath] = DateTime.Now; // Start a timer if not already started
if (!m_timerStarted)
{
m_timer.Change(100, 100);
m_timerStarted = true;
}
}
} private void OnTimeout(object state)
{
List<string> paths; // Don't want other threads messing with the pending events right now
lock (m_pendingEvents)
{
// Get a list of all paths that should have events thrown
paths = FindReadyPaths(m_pendingEvents); // Remove paths that are going to be used now
paths.ForEach(delegate(string path)
{
m_pendingEvents.Remove(path);
}); // Stop the timer if there are no more events pending
if (m_pendingEvents.Count == 0)
{
m_timer.Change(Timeout.Infinite, Timeout.Infinite);
m_timerStarted = false;
}
} // Fire an event for each path that has changed
paths.ForEach(delegate(string path)
{
FireEvent(path);
});
} private List<string> FindReadyPaths(Dictionary<string, DateTime> events)
{
List<string> results = new List<string>();
DateTime now = DateTime.Now; foreach (KeyValuePair<string, DateTime> entry in events)
{
// If the path has not received a new event in the last 75ms
// an event for the path should be fired
double diff = now.Subtract(entry.Value).TotalMilliseconds;
if (diff >= 75)
{
results.Add(entry.Key);
}
} return results;
} private void FireEvent(string path)
{
FileSystemEvent evt = Change;
if (evt != null)
{
evt(path);
}
}
}
}

FileSystemWatcher触发多次Change事件的解决办法 .的更多相关文章

  1. bootstrapValidator关于js,jquery动态赋值不触发验证(不能捕获“程序赋值事件”)解决办法

    //触发oninput事件 //propertychange 兼容ie678 $('#captainName').on('input propertychange', function() { }); ...

  2. 改变input的值不会触发change事件的解决思路

    通常来说,如果我们自己通过 value 改变了 input 元素的值,我们肯定是知道的,但是在某些场景下,页面上有别的逻辑在改变 input 的 value 值,我们可能希望能在这个值发生变化的时候收 ...

  3. input输入框file类型第二次不触发onchange事件的解决办法,简单有效

    在网上看了很多办法,现在将网上大部分说法总结如下: 网上说法: 原因:选择一次后onchange事件没有绑定到input标签上:    解决办法:拷贝一份input标签的副本,每次选择后对原input ...

  4. js设置下拉框选中后change事件无效解决

    下拉框部分代码: <select id="bigType"> <option value="">请选择</option> & ...

  5. jQuery 设置select,radio的值,无法自动触发绑定的change事件

    一.问题 今天在对select和radio做change事件绑定后,手动设置其value值,但是不能触发change事件 二.解决 使用trigger方法手动触发

  6. jquery 怎么触发select的change事件

    可以使用jQuery的trigger() 方法来响应事件 定义和用法 trigger() 方法触发被选元素的指定事件类型. 语法 $(selector).trigger(event,[param1,p ...

  7. 关于webuploader 在ie9上不能触发 input 的 change 事件

    上传文件用了 webuploader,ie9以上及其它浏览器正常执行js ,但是在ie9上点击input 无效,不能触发change 事件. 第一反应是ie9 需要使用flash 上传文件 原因: . ...

  8. 如何触发react input change事件

    页面用react来进行开发的,想触发react组件里面input的change事件,用Jquery的trigger来触发没有效果,必须使用原生的事件来进行触发. var event = new Eve ...

  9. WDatePicker 屏蔽onchange事件的解决办法

    受下面文章的启发,使用DatePicker自带的年月日相关的change事件,可以“勉强”实现input控件的onchange(),直接上代码: 1.第一种方式:利用DatePicker提供的年.月. ...

随机推荐

  1. 转】Maven学习总结(九)——使用Nexus搭建Maven私服

    原博文出自于:http://www.cnblogs.com/xdp-gacl/p/4068967.html 感谢! 一.搭建nexus私服的目的 为什么要搭建nexus私服,原因很简单,有些公司都不提 ...

  2. Linux下的crontab定时执行任务命令详解

    在LINUX中,周期执行的任务一般由cron这个守护进程来处理[ps -ef|grep cron].cron读取一个或多个配置文件,这些配置文件中包含了命令行及其调用时间.cron的配置文件称为“cr ...

  3. 支付结果回调v7核心,投保确认接口..

    <?xml version="1.0" encoding="GBK"?> <PACKET type="REQUEST"&g ...

  4. python知识点 2014-07-09

    迭代协议: 类实现方法__next__,则可以使用for.while来实现迭代 使用iter或者next实现手工迭代: L = [1,2,3] I = iter(L) print(I.next()) ...

  5. Ubuntu下Android编译环境的配置

    从安装操作系统到编译程序结束,过程大致如下. 1. Ubuntu Linux操作系统安装软件包.使用 Ubuntu 14.04 Desktop系统.安装Linux系统到VMWare虚拟机上. 2. 完 ...

  6. JSON如何序列图片

    图片采用JSON格式传输的话,需要把图片转成Base64格式. 首先把图片转成流:Image1.Picture.Graphic.SaveToStream(ss);然后编码成base64格式的:Enco ...

  7. JS中如何判断null、undefined与NaN

    1.判断undefined: <span style="font-size: small;">var tmp = undefined; if (typeof(tmp)  ...

  8. C#上位机读数据库

    string connectionString = string.Format("server=(local);uid=sa;pwd=1234567890;database=msp430&q ...

  9. css 关于两栏布局,左边固定,右边自适应

    好几个星期都没写博客了,最近不忙也不闲,稀里糊涂过了两个星期,之前几个月内天天坚持签到.最近也没签到.哈哈,说正事. 今天做东钿互金平台后台页面,昨天做了一个登录页面,业偶碰到了一个难题.等下也要把它 ...

  10. 用DependanceProperty做Dynamic换Icon

    1:做Icon用Canvas还是DrawingBrush? Canvas的例子: