【问题标题】:One raise for multiple events of FileSystemWatcher一次引发 FileSystemWatcher 的多个事件
【发布时间】:2019-03-25 17:27:01
【问题描述】:

我需要在我将使用FileSystemWatcher 的文件夹中记录文件的创建或复制/移动事件。问题是当我在文件夹中粘贴一个文件时,FileSystemWatcher 将引发一个 Created 事件。因此,如果我将10 文件一起粘贴到该文件夹​​中,FileSystemWatcher 将引发 10 个事件。如果同时复制粘贴在文件夹中的所有 10 个文件,我的要求是只引发一个事件。

请提出建议。以下是我在 MSDN 教程的帮助下编写的代码。

public class FileSystemWatcherUtil2
{

    public static void Main()
    {
        Run();
    }

    [PermissionSet(SecurityAction.Demand, Name = "FullTrust")]
    public static void Run()
    {
        /* creation of a new FileSystemWatcher and set its properties */
        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = @"C:\Users\TestFolder";

        /*watch for internal folder changes also*/
        watcher.IncludeSubdirectories = true;

        /* Watch for changes in LastAccess and LastWrite times, and the renaming of files or directories. */
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;

        /* event handlers */
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Created += new FileSystemEventHandler(OnChanged);
        watcher.Deleted += new FileSystemEventHandler(OnChanged);
        watcher.Renamed += new RenamedEventHandler(OnRenamed);

        /* watching started */
        watcher.EnableRaisingEvents = true;

        /* user should quit the program to stop watching*/
        Console.WriteLine("Press \'q\' to quit the sample.");
        while (Console.Read() != 'q') ;
    }

    /* event handlers definition for changed and renamed */
    private static void OnChanged(object source, FileSystemEventArgs e)
    {
        Console.WriteLine("File: " + e.FullPath + " " + e.ChangeType);
    }

    private static void OnRenamed(object source, RenamedEventArgs e)
    {
        Console.WriteLine("File: {0} renamed to {1}", e.OldFullPath, e.FullPath);
    }

}

【问题讨论】:

    标签: c# .net events


    【解决方案1】:

    这个想法是为FileSystemWatcher 实现一个包装器,它公开自己的CreatedEx 事件,该事件将模拟FileSystemWatcher.Created 事件,但每发生10 次Created 事件就会引发一次。但请记住,我们不能确定所有处理程序Created 事件都在单个复制/移动操作的范围内引发。为了提高这种方法的可靠性,我建议设置一个DirectoryName NotifyFilters value,这样您就可以观察到一个目录,但如果这符合您的要求,那肯定是。也许通过提供有关您的要求的更多详细信息,我们可以提供更多详细信息,因此方法会更可靠,基本上最重要的是如何确定批量复制/移动操作的常见范围,因此当您复制 100 个文件时同时由操作系统启动的其他复制操作正在进行中,您需要从您感兴趣的前一个操作中过滤掉它。所以也许是文件的一些常见标准等。

    1. FileSystemWatcher 实现包装器
    2. 公开自己的CreatedEx事件
    3. 订阅FileSystemWatcher.Created 事件并在处理程序中启动一个计时器,例如200 毫秒。如果未引发 FileSystemWatcher.Created 事件 - 引发自己的 CreatedEx 事件,否则重置计时器并等待 200 毫秒。

    定时器的时间段你可以通过实验的方式确定,复制多个不同大小的文件,看看哪个超时时间就够了。

    如果您需要为恰好 10 个同时发生的事件引发一次 CreatedEx 事件 - 您可以在 FileSystemWatcher.Created 事件处理程序中引入简单的计数器和递减,并且当 counter==0 时 - 引发自己的 CreatedEx 事件。

    【讨论】:

    • 我认为 OP 唯一真正的答案是你不能,10 个文件创建 10 个事件。它是 FileSystemWatcher,而不是 PasteWatcher ;)
    • @Lazarus :您可以使用包装器,每 10 次内部 Created 事件发生一次,它会引发自己的 CreatedEx 一次
    • 这不是仍然依赖于在彼此的超时期限内发生的 10 个内部 Created 事件(在您的示例中为 200 毫秒)吗?您真的能够确定这 10 个事件是单个粘贴活动的结果,而不是批处理文件复制文件的结果吗?
    • @Lazarus : 对,我们不能 100% 确定这是一个单一的批量复制/移动操作,将这一点添加到答案中,谢谢
    • 可以是单次粘贴,也可以是批量复制。这将取决于用户如何在该文件夹中进行更改。
    【解决方案2】:

    FileSystemWatcher 无法知道复制是否作为一个操作完成,因为没有一次复制多个文件这样的事情。 Windows 资源管理器使用拖放操作看起来像这样,但它本质上是多个副本。

    如果您愿意,您可以围绕 FileSystemWatcher 编写一个简单的包装器,该包装器将接受所有 Create 事件,如果它们发生,例如相隔 200 毫秒,则引发“Multiple Created”事件。

    这是一个监控 Create 事件的工作代码。如果您需要和/或公开其他属性,您可以基于它来监控其他事件。

    public class MyWatcher
    {
        private FileSystemWatcher watcher = new FileSystemWatcher();
        private System.Timers.Timer timer;
        private object listSync = new object();
        private List<FileSystemEventArgs> events = new List<FileSystemEventArgs>();
    
        public delegate void FileSystemMultipleEventHandler(object sender, FileSystemEventArgs[] events);
        public FileSystemMultipleEventHandler OnMultipleFilesCreated { get; set; }
    
        public MyWatcher(string path, NotifyFilters notifyFilters, string filter)
        {
            this.watcher.Path = path;
            this.watcher.Created += FileCreated;
            watcher.Filter = filter;
            watcher.EnableRaisingEvents = true;
        }
    
        private void FileCreated(object sender, FileSystemEventArgs e)
        {
            if (this.timer != null)
            {
                this.timer.Stop();
                lock (this.listSync) this.events.Add(e);
                this.timer.Start();
            }
            else
            {
                lock (this.listSync) this.events.Add(e);
                this.timer = new Timer(200);
                this.timer.Elapsed += MultipleFilesCreated;
                this.timer.Start();
            }
        }
    
        private void MultipleFilesCreated(object stat, ElapsedEventArgs args)
        {
            if (OnMultipleFilesCreated != null)
            {
                FileSystemEventArgs[] result;
                lock (this.listSync)
                {
                    // make a copy
                    result = events.ToArray();
                    this.events = new List<FileSystemEventArgs>();
                }
                OnMultipleFilesCreated(this, result);
            }
            this.timer.Stop();
        }
    }
    

    我在这里使用锁定来访问线程安全的事件列表(计时器与 FileCreated 事件)。不使用任何并发集合,因为它们没有 ClearAll(),我需要一个一个地锁定和删除项目,或者无论如何都要重新创建带锁的集合。

    【讨论】:

    • 谢谢 Maciej,我也想试试你的方法。
    • 当其中一个文件非常大并且复制时间超过我们的默认时间(比如 200 毫秒)时,此方法将失败
    猜你喜欢
    • 1970-01-01
    • 2010-12-18
    • 1970-01-01
    • 2023-03-21
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多