簡體   English   中英

將FileSystemWatcher與多個文件一起使用

[英]Using FileSystemWatcher with multiple files

我想使用FileSystemWatcher來監視目錄及其子目錄中移動的文件。 然后我想在移動所有文件時觸發一些代碼。 但我不知道怎么做。 我的代碼將在每次移動文件時觸發,如果用戶一次移動多個文件,我只希望它為所有文件觸發一次。 所以基本上我想要創建一個列表,一旦完成所有文件的移動,我想對該列表做一些事情......

這是代碼:

class Monitor
{
    private List<string> _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<string> _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<string>();
        }


        // 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<string> FileList { get; set; }
}

public class Monitor
{
    private List<string> 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<string>();

        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<string> list = new List<string>();
        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語句中,這是有效的。 我不知道是否有某種原因我不想那樣做?

就像傑伊所說:計時器可能是“分組”事件的唯一方式。 鎖定可能有點過分,但我不喜歡在多線程情況下改變集合的想法(我認為來自fswatcher的事件是在池中的線​​程上調用的)。

  public class Monitor : IDisposable
  {
     private List<string> filePaths;
     private ReaderWriterLockSlim rwlock;
     private Timer processTimer;
     private string watchedPath;
     private FileSystemWatcher watcher;

     public Monitor(string watchedPath)
     {
        filePaths = new List<string>();

        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

編輯:現在包含觀察者的錯誤處理,處置邏輯。

我不得不做同樣的事情。 在Monitor類中使用System.Timers.Timer並對其Elapsed事件進行編碼以處理文件列表並清除List。 當第一項通過FSW事件添加到文件列表中時,啟動計時器。 當后續項目添加到列表'重置'時,通過停止並重新啟動它。

像這樣的東西:

class Monitor
{
    FileSystemWatcher _fsw;
    Timer _notificationTimer;
    List<string> _filePaths = new List<string>();

    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<string>();
    }


    // 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<Unit> OnUpdate(string path, string pattern)
        {
            return Observable.Create<Unit>(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; //最大緩沖區大小

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM