FileSystemWatcher触发多次Change事件的解决办法

 最近要用到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 the Archived 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:

解决方案代码

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用法详解

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
FileSystemWatcher是一个用于监视文件系统更改的类。在C#中,可以通过以下步骤来使用FileSystemWatcher类: 1. 首先,在C#代码中引入System.IO命名空间,该命名空间包含了FileSystemWatcher类。 2. 实例化一个FileSystemWatcher对象,并设置要监视的文件或目录。 ```csharp FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Path = @"C:\Path\To\Directory"; ``` 3. 设置要监视的文件类型。可以使用通配符"*"来监视所有文件,或者使用特定的文件扩展名。 ```csharp watcher.Filter = "*.txt"; // 监视所有txt文件 ``` 4. 设置要监视的文件系统更改类型,如创建、删除、重命名或修改。 ```csharp watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite | NotifyFilters.Attributes; ``` 5. 设置是否监视子目录中的更改。 ```csharp watcher.IncludeSubdirectories = true; // 监视子目录中的更改 ``` 6. 在需要时,注册事件处理程序来处理文件系统更改时触发事件。 ```csharp watcher.Created += OnFileCreated; watcher.Deleted += OnFileDeleted; watcher.Renamed += OnFileRenamed; ``` 7. 实现事件处理程序来处理文件系统更改触发事件。 ```csharp private static void OnFileCreated(object source, FileSystemEventArgs e) { Console.WriteLine("File Created: " + e.Name); } private static void OnFileDeleted(object source, FileSystemEventArgs e) { Console.WriteLine("File Deleted: " + e.Name); } private static void OnFileRenamed(object source, RenamedEventArgs e) { Console.WriteLine("File Renamed: " + e.OldName + " to " + e.Name); } ``` 8. 启动FileSystemWatcher对象以开始监视文件系统更改。 ```csharp watcher.EnableRaisingEvents = true; ``` 现在,当指定目录中的文件被创建、删除或重命名时,相应的事件处理程序将触发并执行定义的逻辑。 这是使用C#中FileSystemWatcher类来监视文件系统更改的基本过程。根据具体的需求,可以进一步定制和扩展功能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值