将FileSystemWatcher与多个文件一起使用

我想使用FileSystemWatcher来监视目录及其子目录中移动的文件。 然后我想在移动所有文件时触发一些代码。 但我不知道怎么做。 我的代码将在每次移动文件时触发,如果用户一次移动多个文件,我只希望它为所有文件触发一次。 所以基本上我想创建一个列表,一旦完成所有文件的移动,我想要对该列表进行操作……

这是代码:

class Monitor { private List _filePaths; public void CreateWatcher(string path) { FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Filter = "*.*"; watcher.Created += new FileSystemEventHandler(watcher_FileCreated); watcher.Path = path; watcher.IncludeSubdirectories = true; watcher.EnableRaisingEvents = true; } void watcher_FileCreated(object sender, FileSystemEventArgs e) { _filePaths.Add(e.FullPath); Console.WriteLine("Files have been created or moved!"); } } 

更新:尝试使用Chris的代码,但它不起作用(请参阅我在Chris的回答中的评论):

 class Monitor { private List _filePaths; private Timer _notificationTimer; private FileSystemWatcher _fsw; public Monitor(string path) { _notificationTimer = new Timer(); _notificationTimer.Elapsed += notificationTimer_Elapsed; // CooldownSeconds is the number of seconds the Timer is 'extended' each time a file is added. // I found it convenient to put this value in an app config file. int CooldownSeconds = 1; _notificationTimer.Interval = CooldownSeconds * 1000; _fsw = new FileSystemWatcher(); _fsw.Path = path; _fsw.IncludeSubdirectories = true; _fsw.EnableRaisingEvents = true; // Set up the particulars of your FileSystemWatcher. _fsw.Created += fsw_Created; } private void notificationTimer_Elapsed(object sender, ElapsedEventArgs e) { // // Do what you want to do with your List of files. // Console.Write("Done"); // Stop the timer and wait for the next batch of files. _notificationTimer.Stop(); // Clear your file List. _filePaths = new List(); } // Fires when a file is created. private void fsw_Created(object sender, FileSystemEventArgs e) { // Add to our List of files. _filePaths.Add(e.Name); // 'Reset' timer. _notificationTimer.Stop(); _notificationTimer.Start(); } } 

更新2:

根据安德斯的回答试过这个:

 public class FileListEventArgs : EventArgs { public List FileList { get; set; } } public class Monitor { private List filePaths; private ReaderWriterLockSlim rwlock; private Timer processTimer; public event EventHandler FileListCreated; public void OnFileListCreated(FileListEventArgs e) { if (FileListCreated != null) FileListCreated(this, e); } public Monitor(string path) { filePaths = new List(); rwlock = new ReaderWriterLockSlim(); FileSystemWatcher watcher = new FileSystemWatcher(); watcher.Filter = "*.*"; watcher.Created += watcher_FileCreated; watcher.Path = path; watcher.IncludeSubdirectories = true; watcher.EnableRaisingEvents = true; } private void ProcessQueue() { List list = new List(); try { Console.WriteLine("Processing queue, " + filePaths.Count + " files created:"); rwlock.EnterReadLock(); } finally { if (processTimer != null) { processTimer.Stop(); processTimer.Dispose(); processTimer = null; OnFileListCreated(new FileListEventArgs { FileList = filePaths }); filePaths.Clear(); } rwlock.ExitReadLock(); } } void watcher_FileCreated(object sender, FileSystemEventArgs e) { try { rwlock.EnterWriteLock(); filePaths.Add(e.FullPath); if (processTimer == null) { // First file, start timer. processTimer = new Timer(2000); processTimer.Elapsed += (o, ee) => ProcessQueue(); processTimer.Start(); } else { // Subsequent file, reset timer. processTimer.Stop(); processTimer.Start(); } } finally { rwlock.ExitWriteLock(); } } 

我不得不将事件触发器移动到finally语句中,这是有效的。 我不知道是否有某种原因我不想这样做?

就像杰伊所说:计时器可能是“分组”事件的唯一方式。 锁可能有点过分,但我不喜欢在multithreading情况下改变集合的想法(我认为来自fswatcher的事件是在池中的线​​程上调用的)。

  public class Monitor : IDisposable { private List filePaths; private ReaderWriterLockSlim rwlock; private Timer processTimer; private string watchedPath; private FileSystemWatcher watcher; public Monitor(string watchedPath) { filePaths = new List(); rwlock = new ReaderWriterLockSlim(); this.watchedPath = watchedPath; InitFileSystemWatcher(); } private void InitFileSystemWatcher() { watcher = new FileSystemWatcher(); watcher.Filter = "*.*"; watcher.Created += Watcher_FileCreated; watcher.Error += Watcher_Error; watcher.Path = watchedPath; watcher.IncludeSubdirectories = true; watcher.EnableRaisingEvents = true; } private void Watcher_Error(object sender, ErrorEventArgs e) { // Watcher crashed. Re-init. InitFileSystemWatcher(); } private void Watcher_FileCreated(object sender, FileSystemEventArgs e) { try { rwlock.EnterWriteLock(); filePaths.Add(e.FullPath); if (processTimer == null) { // First file, start timer. processTimer = new Timer(2000); processTimer.Elapsed += ProcessQueue; processTimer.Start(); } else { // Subsequent file, reset timer. processTimer.Stop(); processTimer.Start(); } } finally { rwlock.ExitWriteLock(); } } private void ProcessQueue(object sender, ElapsedEventArgs args) { try { Console.WriteLine("Processing queue, " + filePaths.Count + " files created:"); rwlock.EnterReadLock(); foreach (string filePath in filePaths) { Console.WriteLine(filePath); } filePaths.Clear(); } finally { if (processTimer != null) { processTimer.Stop(); processTimer.Dispose(); processTimer = null; } rwlock.ExitReadLock(); } } protected virtual void Dispose(bool disposing) { if (disposing) { if (rwlock != null) { rwlock.Dispose(); rwlock = null; } if (watcher != null) { watcher.EnableRaisingEvents = false; watcher.Dispose(); watcher = null; } } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } } 

记得在fswatcher上设置缓冲区大小并实现fswatcher的“复活”(如果它出错)(即将错误事件绑定到重新创建观察者的方法)。

编辑:注意,此示例中的计时器是System.Timers.Timer,而不是System.Threading.Timer

编辑:现在包含观察者的error handling,处置逻辑。

我不得不做同样的事情。 在Monitor类中使用System.Timers.Timer并对其Elapsed事件进行编码以处理文件列表并清除列表。 当第一项通过FSW事件添加到文件列表中时,启动计时器。 当后续项目被添加到列表’重置’时,通过停止并重新启动它。

像这样的东西:

 class Monitor { FileSystemWatcher _fsw; Timer _notificationTimer; List _filePaths = new List(); public Monitor() { _notificationTimer = new Timer(); _notificationTimer.Elapsed += notificationTimer_Elapsed; // CooldownSeconds is the number of seconds the Timer is 'extended' each time a file is added. // I found it convenient to put this value in an app config file. _notificationTimer.Interval = CooldownSeconds * 1000; _fsw = new FileSystemWatcher(); // Set up the particulars of your FileSystemWatcher. _fsw.Created += fsw_Created; } private void notificationTimer_Elapsed(object sender, ElapsedEventArgs e) { // // Do what you want to do with your List of files. // // Stop the timer and wait for the next batch of files. _notificationTimer.Stop(); // Clear your file List. _filePaths = new List(); } // Fires when a file is created. private void fsw_Created(object sender, FileSystemEventArgs e) { // Add to our List of files. _filePaths.Add(e.Name); // 'Reset' timer. _notificationTimer.Stop(); _notificationTimer.Start(); } } 

Rx – 油门 – 使这项工作变得简单。 这是一个可测试的,可重复使用的解决方案。

 public class Files { public static FileSystemWatcher WatchForChanges(string path, string filter, Action triggeredAction) { var monitor = new FileSystemWatcher(path, filter); //monitor.NotifyFilter = NotifyFilters.FileName; monitor.Changed += (o, e) => triggeredAction.Invoke(); monitor.Created += (o, e) => triggeredAction.Invoke(); monitor.Renamed += (o, e) => triggeredAction.Invoke(); monitor.EnableRaisingEvents = true; return monitor; } } 

允许将事件合并为单个序列

  public IObservable OnUpdate(string path, string pattern) { return Observable.Create(o => { var watcher = Files.WatchForChanges(path, pattern, () => o.OnNext(new Unit())); return watcher; }); } 

最后,用法

 OnUpdate(path, "*.*").Throttle(Timespan.FromSeconds(10)).Subscribe(this, _ => DoWork()) 

你显然可以使用油门速度来满足你的需求。

此外,将缓冲区大小设置为大于默认值以避免缓冲区溢出。 如果在源目录中删除了超过25个文件(在我的测试中),就会发生这种情况。 如果删除了200个文件,则仅调用几个文件的事件处理程序,而不是所有文件。

_watcher.InternalBufferSize = 65536; //最大缓冲区大小