Je souhaite utiliser FileSystemwatcher pour surveiller un répertoire et ses sous-répertoires pour les fichiers déplacés. Et puis je veux déclencher du code lorsque tous les fichiers ont été déplacés. Mais je ne sais pas comment. Mon code tel que déclencher chaque fois qu'un fichier est déplacé et si un utilisateur déplace plusieurs fichiers à une fois, je veux seulement que cela déclenche une fois pour tous les fichiers. Donc, fondamentalement, je souhaite créer une liste, et une fois que le déménagement de tous les fichiers est effectué, je veux faire des choses à cette liste ...
Voici le code: p> Mise à jour: essayer d'utiliser le code de Chris, mais cela ne fonctionne pas (voir mon commentaire à la réponse de Chris): p> mise à jour 2: p> Essayé Ceci selon la réponse d'Anders: P> 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();
}
}
4 Réponses :
Je devais faire exactement la même chose. Utilisez un System.Timers.Timer dans votre classe Monitor et coder son événement écoulé pour traiter votre liste de fichiers et effacer la liste. Lorsque le premier élément est ajouté à votre liste de fichiers via les événements FSW, lancez la minuterie. Lorsque des éléments suivants sont ajoutés à la liste "Réinitialiser" la minuterie en l'arrêtant et en le redémarrant.
quelque chose comme ceci: p>
Merci Jay, mais je ne suis pas sûr. Je peux vous mal comprendre, mais peut-être que je n'ai pas expliqué la situation suffisamment. Je ne vois pas comment ce code sera jamais appelé. Parce que ce qui se passe, c'est que l'application ne sera lancée qu'une seule fois, puis il surveillera tout ce qui se produit dans un répertoire. Donc, la seule chose qui puisse faire quoi que ce soit, c'est si l'événement créé est déclenché. Je ne peux pas démarrer l'application et appeler manuellement la méthode qui commence la minuterie. Cela signifierait que le code ne fonctionnerait que pour les premières secondes après le démarrage de la demande? Ou dois-je vous mal comprendre?
@Anders La minuterie est arrêtée par défaut et démarre lorsque l'événement créé FSW incendie. Lorsque la minuterie écoulée l'événement incendie, la minuterie s'arrête elle-même. J'ajouterai que mon code est continuellement en cours d'exécution dans un service Windows. Lorsque le service commence l'application commence et continue à fonctionner jusqu'à ce que le service s'arrête.
Ok, je dois faire quelque chose de mal, car je l'ai essayé, légèrement modifié pour travailler avec le reste de mon code, mais cela ne fonctionne pas. Si je déplace un fichier, le code est déclenché. Mais seulement une fois. La prochaine fois que je déplace un fichier, le code ne déclenche pas. Et quand j'essaie de bouger plusieurs, il n'atteint jamais l'événement écoulé. Voir la mise à jour!
Comme Jay dit: une minuterie est probablement le seul moyen de "grouper" événements. La serrure peut être surchargée, mais je n'aime pas l'idée de muter une collection dans une situation multithread (je pense que les événements de la FSWatcher sont appelés des threads à partir d'une piscine).
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); } }
Ok, merci, cela semble presque travailler, mais je ne suis pas sûr de ce qui se passe avec tous les serrures de lecture et de cette affaire. Il arrive à la première fois que tous les fichiers répertoriés dans la liste FilePaths. Mais ensuite, cela commence à aller dans une boucle étrange sautant d'avant en arrière dans le code. Qu'est-ce qui ne va pas? Encore une fois, il semble avoir les bonnes informations la première fois qu'elle y arrive, mais alors ça commence à aller d'avant en arrière ... ne comprend pas pourquoi ...
J'ai mis à jour l'échantillon de code (a raté un arrêt de départ et d'arrêt) et l'a testé. Semble fonctionner ok maintenant. Le verrou est juste là, il n'y a donc aucun fichier ajouté à la file d'attente, lors du traitement de la file d'attente. Les événements FSWatcher qui se produisent lors du traitement du dernier lot, attendez-vous et sont ajoutés au lot suivant.
Merci, il semble fonctionner bien maintenant! Quelques questions, si cela ne vous dérange pas: d'abord, comment manipulez-moi de recréer l'observateur dans une déclaration d'essai? Où? Et que se passe-t-il ici: processtigy.elapsed + = (o, ee) => Processqueur (); Merci!
J'ai changé la confusion (O, EE) à un délégué normal (équivalent). J'ai ajouté le code pour réinitialiser l'observateur après une défaillance et un code pour disposer correctement l'observateur.
D'accord, super. Juste une chose de plus: lorsque j'essaie d'élever un événement après la fin de la liste afin que je puisse le retourner à la winform, il semble que le processus est appelé plusieurs fois (un pour chaque fichier?), Tandis que si je n'ai pas de événement déclenché là-bas (juste le forach de votre exemple), alors il ne semble pas être appelé plus d'une fois ... Voici ce que je substituait le pourcheach avec: var fileSteventargs = nouveau Filelisteventargs {Filelist = FilePaths}; OnfilelistCreated (Filelisteventargs); Une idée pourquoi?
Correction, ce n'était pas le nombre de fichiers, il semble que cela semble être appelé plusieurs fois plus que cela ... de sorte que l'événement personnalisé est déclenché plusieurs fois au lieu d'une seule fois ...
Aucune idée, je ne peux pas reproduire ça. J'obtiens une invocation du gestionnaire d'événements si j'ajoute un événement comme vous décrivez. Vérifiez deux fois que vous n'avez pas abonné à l'événement plus d'une fois. Si vous vous abonnez à FilelistCreated deux fois, vous obtiendrez le gestionnaire d'événements appelé deux fois à chaque fois.
Non, cela semble en fait aller dans une boucle infinie, je dois arrêter le débogueur pour arrêter les appels ... mais je l'ai fait travailler si je déplace la clairière de la liste et que l'événement déclenche dans la déclaration enfin. Est-ce faux pour une raison quelconque? Cela semble bien fonctionner de toute façon! Voir ma mise à jour.
Décidé que cela est répondu maintenant. J'ai une question de suivi si vous pensez que vous pouvez vous aider aussi à cela aussi: Stackoverflow.com/Questtions/6950071/...
rx - papillon - rend ce travail facile. Voici une solution testable et réutilisable.
OnUpdate(path, "*.*").Throttle(Timespan.FromSeconds(10)).Subscribe(this, _ => DoWork())
Aussi, définissez la taille de la mémoire tampon supérieure à celle par défaut pour éviter le débordement de la mémoire tampon. Cela se produit lorsque plus de 25 fichiers sont abandonnés dans le répertoire de source (dans mon test). Si 200 fichiers ont chuté, le gestionnaire d'événements n'est appelé que peu de fichiers, pas tous. P>
_watcher.internalbufferSize = 65536; // taille maximale de tampon p>
Obtiendrez-vous des événements dans le répertoire où le fichier est supprimé / ajouté, ainsi que pour le (s) fichier (s) déplacé (s)? Si tel est le cas, vous pourriez peut-être ignorer (ou filtrer) les événements de fichiers et écouter uniquement les événements d'annuaire? Edit: Vous obtenez probablement plusieurs événements ajoutés dans le répertoire également, si plusieurs fichiers sont ajoutés, non? : /
Je ne suis pas tout à fait sûr de ce que vous demandez ici. Voulez-vous remplir la liste au point que le code commence et attendez ensuite jusqu'à ce que tout soit déplacé? Ou allez-vous remplir la liste progressivement?
@Chris: Eh bien, dites à un utilisateur sélectionne un certain nombre de fichiers et les déplace vers un autre sous-répertoire. Dans ce cas, l'événement déclenchera pour chaque fichier (qui déclenchera à la fois un événement de suppression et de création, et j'utilise l'événement Créer pour détecter les fichiers déplacés). Donc, même si chacun des fichiers que l'utilisateur se déplace en une seule opération déclenchera l'événement, je veux seulement gérer l'événement une fois que tous les fichiers ont été déplacés.
@Anders: Eh bien, je ne suis pas sûr de comprendre votre question pleinement, je suis tout à fait nouveau dans le fichier de fichiers Systemwatcher, mais oui, je suppose ce que vous dites est correct, qu'il devrait y avoir plusieurs déclencheurs dans les deux, donc je suis Je ne sais pas comment le résoudre par ça ...
Ah ok. On dirait que la réponse de Jay est la voie à suivre. J'ai toujours trouvé que le fichiersSystemwatcher soit un peu floconneux de toute façon!
@Chris je suis d'accord! J'ai toujours pensé que le FSW devrait être plus facile à travailler avec alors c'est. J'ai trouvé Cet article (et sa deuxième partie) à CodeProject utile.