FileSystemWatcher多次触发事件的强大解决方案

时间:2017-11-13 21:08:38

标签: c# filesystemwatcher

FileSystemWatcher个事件可以多次触发。如果我需要从代码中获得可预测的行为,那就不好了。

MSDN documentation

中对此进行了描述
  

通用文件系统操作可能会引发多个事件。对于   例如,当一个文件从一个目录移动到另一个目录时,有几个   可能会引发OnChanged和一些OnCreated和OnDeleted事件。   移动文件是一个复杂的操作,由多个简单组成   操作,因此提出多个事件。同样,一些   应用程序(例如,防病毒软件)可能会导致其他问题   FileSystemWatcher检测到的文件系统事件。

在特定事件中充分利用NotifyFilters有所帮助,但却没有让我对这种一致性有100%的信心。

以下是一个示例,重新创建一个记事本写入示例(但我也经历过其他写操作):

 public ExampleAttributesChangedFiringTwice(string demoFolderPath)
 {
     var watcher = new FileSystemWatcher()
     {
          Path = @"c:\temp",
          NotifyFilter = NotifyFilters.LastWrite,
          Filter = "*.txt"
      };

      watcher.Changed += OnChanged;
      watcher.EnableRaisingEvents = true;
  }

  private static void OnChanged(object source, FileSystemEventArgs e)
  {
      // This will fire twice if I edit a file in Notepad
  }

是否有任何建议使其更具弹性?

编辑:意味着在触发多个事件时不会重复多个操作。

2 个答案:

答案 0 :(得分:1)

一种利用MemoryCache作为缓冲区的方法,该方法将会扼杀'其他活动。

  1. 触发文件事件(本例中为Changed
  2. 该事件由OnChanged处理,但不是完成所需的操作,而是将事件存储在MemoryCache中  1秒到期  以及在到期时执行的CacheItemPolicy回调设置。
  3. 请注意,我使用AddOrGetExisting作为一种简单的方法来阻止在缓存期内触发的任何其他事件被添加到缓存中。

    1. 当它到期时,回调OnRemovedFromCache完成了针对该文件事件的行为
    2.   class BlockAndDelayExample
      {
          private readonly MemoryCache _memCache;
          private readonly CacheItemPolicy _cacheItemPolicy;
          private const int CacheTimeMilliseconds = 1000;
      
          public BlockAndDelayExample(string demoFolderPath)
          {
              _memCache = MemoryCache.Default;
      
              var watcher = new FileSystemWatcher()
              {
                  Path = demoFolderPath,
                  NotifyFilter = NotifyFilters.LastWrite,
                  Filter = "*.txt"
              };
      
              _cacheItemPolicy = new CacheItemPolicy()
              {
                  RemovedCallback = OnRemovedFromCache
              };
      
              watcher.Changed += OnChanged;
              watcher.EnableRaisingEvents = true;
          }
      
          // Add file event to cache for CacheTimeMilliseconds
          private void OnChanged(object source, FileSystemEventArgs e)
          {
              _cacheItemPolicy.AbsoluteExpiration =
                  DateTimeOffset.Now.AddMilliseconds(CacheTimeMilliseconds);
      
              // Only add if it is not there already (swallow others)
              _memCache.AddOrGetExisting(e.Name, e, _cacheItemPolicy);
          }
      
          // Handle cache item expiring
          private void OnRemovedFromCache(CacheEntryRemovedArguments args)
          {
              if (args.RemovedReason != CacheEntryRemovedReason.Expired) return;
      
              // Now actually handle file event
              var e = (FileSystemEventArgs) args.CacheItem.Value;
          }
      }
      

      可以轻松扩展到:

      • 从缓存中检查文件锁定到期时如果不可用,请将其重新放回缓存中(有时事件会激活,文件不准备进行某些操作)。最好使用try / catch循环。
      • 文件名上的密钥缓存+事件类型合并

答案 1 :(得分:1)

我使用FileSystemWatcher检查上传的MP4文件,我最终必须做些什么。进行上传的过程似乎没有对文件建立任何锁定,所以我以前很难过早地开始处理它们。

我最终采用的技术,在我的案例中完全成功,就是使用事件并将文件路径添加到Dictionary<string, long>可能有趣的文件中,并启动计时器。我定期(60秒)检查文件大小。 long字典值保存上次检查时的文件大小,如果当前大小更大,我认为它仍然被写入,存储新大小并再次休眠60秒。

如果没有写入活动的时间为60秒,我可以开始处理。

如果这不适合你,你可以考虑其他一些事情;每分钟散列文件并存储散列,定期重新散列,直到内容没有改变。密切关注文件系统中的上次修改日期,也许

最后,请考虑FileSYstemWatcher可能是一个有用的设备,不是用于通知您必须处理哪些文件,而是用于可能感兴趣的文件,并且具有更精细的内部逻辑的单独进程可以决定是否可能有趣的文件应该采取行动