Utilizzando FileSystemWatcher con più file

Voglio usare il FileSystemWatcher per il monitoraggio di una directory e sottodirectory per i file che vengono spostati. E poi voglio innescare un po ‘ di codice quando tutti i file sono stati spostati. Ma non so come. Il mio codice è attiverà ogni volta che un file viene spostato, e se un utente si muove più file in una sola volta io lo voglio solo per attivare una volta per tutti i file. Quindi, in pratica voglio creare un elenco, e una volta che il movimento di tutti i file è fatto voglio fare le cose alla lista…

Ecco il codice:

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!");
    }

}

AGGIORNAMENTO: Cercando di utilizzare Chris codice, ma non funziona (vedi il mio commento di Chris risposta):

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


    }

AGGIORNA 2:

Provato questo secondo Anders risposta:

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

Ho dovuto spostare l’evento di trigger in infine istruzione, e che funziona. Non so se c’è qualche motivo non vorrei fare che?

Si ottiene eventi dalla directory in cui il file è stato rimosso/aggiunto, nonché per spostare file(s)? Se è così, forse si potrebbe semplicemente ignorare (o filtro) il file di eventi e ascoltare solo la directory di eventi? Edit: probabilmente ottenere più add-eventi dalla directory, se più file che vengono aggiunti, giusto? :/
Io non sono molto sicuro di quello che stai chiedendo qui. Vuoi compilare l’elenco a punto il codice di avvio e quindi attendere fino a quando tutto è spostato? O sarebbe compilare l’elenco gradualmente?
beh, dire che un utente seleziona un numero di file e li muove ad un’altra sottodirectory. In questo caso l’evento di trigger per ogni file (che attiverà sia eliminare e creare l’evento, e sto usando il creare l’evento per il rilevamento di file spostato). Così, anche se ogni file che l’utente si muove in una sola operazione attiverà l’evento, voglio solo per gestire l’evento, una volta che tutti i file sono stati spostati.
beh, io non sono sicuro di aver capito la tua domanda completamente, sono abbastanza nuovo per il FileSystemWatcher, ma sì, presumo che quello che stai dicendo è corretto, che non ci dovrebbe essere più attiva in entrambi, quindi non sono sicuro di come risolvere tramite…
Ah, OK. Sembra di Jay risposta è la strada da percorrere. Ho sempre trovato il filesystemwatcher di essere un po ‘ traballante comunque!

InformationsquelleAutor Anders | 2011-08-04

4 Replies
  1. 10

    Come Jay dice: un timer è probabilmente l’unico modo per “gruppo” eventi. Il blocco può essere eccessivo, ma non mi piace l’idea della mutazione di una raccolta, in un multithread situazione (penso che gli eventi dal fswatcher sono chiamati thread da un pool).

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

    Ricordarsi di impostare la dimensione del buffer sul tuo fswatcher E implementare la “resurrezione” del fswatcher se si ottiene un errore (cioè associare l’evento di errore di un metodo che ricrea l’osservatore).

    Edit: nota, il timer in questo esempio è un Sistema.Timer.Timer, non un Sistema.La filettatura.Timer

    Edit: Ora contiene un errore di gestione per l’osservatore, smaltire logica.

    Ok, grazie, questo sembra quasi di lavoro, ma io non sono sicuro di quello che sta succedendo con tutti i blocchi di lettura e di quella roba. Essa consente di ottenere il ProcessQueue la prima volta con tutti i file elencati in i percorsi di elenco. Ma poi si comincia ad andare in qualche strano anello di salto avanti e indietro nel codice. Cosa c’è di sbagliato? Di nuovo, non sembra avere le giuste informazioni la prima volta che ci arriva, ma poi si comincia ad andare avanti e indietro… non capisco perché…
    Ho aggiornato il codice di esempio (perso una start & stop), e testato. Sembra funzionare bene ora. Il blocco è solo lì, quindi non ci sono i file aggiunti alla coda, durante l’elaborazione della coda. Il fswatcher eventi che si verificano durante l’esecuzione dell’ultimo lotto semplicemente aspettare e vengono aggiunti alla prossima partita.
    Grazie, sembra funzionare bene ora! Solo un paio di domande, se non ti dispiace: in Primo luogo, come faccio a gestire ricreare l’osservatore, in una dichiarazione di prova? Dove? E quello che sta succedendo qui: processTimer.Trascorso += (o, ee) => ProcessQueue(); Grazie!
    Ho cambiato la confusione (o,ee) per un normale (equivalenti) del delegato. Ho aggiunto il codice per reinit watcher dopo un errore, e un po ‘ di codice per smaltire correttamente l’osservatore.
    Ok, grande. Solo una cosa: quando si tenta di generare un evento dopo l’elenco è finito così posso tornare al winform, sembra che il ProcessQueue è chiamato diverse volte (una per ogni file?), mentre se non si dispone di un evento (solo foreach del tuo esempio), quindi non mi pare di essere chiamato più di una volta… Ecco cosa ho sostituiti foreach con: var fileListEventArgs = new FileListEventArgs {FileList = i percorsi}; OnFileListCreated(fileListEventArgs); Qualche idea del perché?

    InformationsquelleAutor Anders Forsgren

  2. 4

    Ho dovuto fare la stessa cosa. Utilizzo di un Sistema.Timer.Timer nel Monitor di classe di codice e il suo tempo Trascorso evento per elaborare la vostra Lista di file e cancella Elenco. Quando il primo elemento è stato aggiunto alla tua Lista di file via FSW eventi, avviare il Timer. Quando gli elementi successivi sono aggiunti alla lista ‘reset’ il Timer, l’arresto e il riavvio.

    Qualcosa di simile a questo:

    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();
        }
    }
    Ok, grazie, potrebbe dare un esempio di codice, per favore?
    Speranza che aiuta.
    Grazie Jay, ma non ne sono sicuro. Mi potrebbe fraintendere, ma forse non mi sono spiegato la situazione e ‘ abbastanza. Non vedo come questo codice verrà mai chiamato. Perché quello che succede è che l’applicazione sarà avviata solo una volta, e quindi è in grado di monitorare tutto ciò che accade in una directory. Quindi l’unica cosa che può fare qualunque cosa accada è che se l’evento viene attivato. Non posso avviare l’applicazione manualmente e chiamare il metodo che avvia il timer. Che significa il codice funziona solo per i primi secondi dopo l’avvio dell’applicazione? O non mi fraintendere?
    Il Timer non è attivo di default, e inizia quando il FSW evento Creato incendi. Quando il Timer Trascorso evento viene attivato il Timer si ferma. Vorrei aggiungere che il mio codice è in esecuzione in un Servizio di Windows. Avvio del servizio, l’applicazione si avvia e continua a funzionare fino a quando si arresta il servizio.
    Ok, devo fare qualcosa di sbagliato, perchè io ho provato, leggermente modificato per funzionare con il resto del mio codice, ma non funziona. Se sposto un file il codice è attivato. Ma solo una volta. La prossima volta che mi sposto un file di codice non attiva. E quando cerco di spostare più, non raggiunge mai la durata dell’evento. Vedere aggiornamento!

    InformationsquelleAutor Jay Riggs

  3. 3

    Rx – throttle – rende questo processo facile. Qui è testabile, soluzione riutilizzabile.

    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;
                }
    }

    Permette di unire in un unica sequenza

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

    infine, l’utilizzo

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

    u può ovviamente giocare con l’acceleratore di velocità per soddisfare le vostre esigenze.

    questo è il modo di andare… funziona come un fascino…

    InformationsquelleAutor Weq

  4. 2

    Inoltre, impostare la dimensione del buffer di dimensioni maggiori di default per evitare l’overflow del buffer. Succede quando più di 25 file vengono eliminati nella directory di origine (nel mio test). Se 200 file eliminati, gestore di evento viene chiamato solo per alcuni file, non tutti.

    _watcher.InternalBufferSize = 65536; //dimensione massima del buffer

    InformationsquelleAutor HirenP

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *