J'ai une application dans laquelle je recherche un fichier texte et s'il y a des modifications apportées au fichier, j'utilise le OnChanged
gestionnaire d'événements pour gérer l'événement. J'utilise le NotifyFilters.LastWriteTime
mais l'événement est toujours renvoyé deux fois. Voici le code.
public void Initialize()
{
FileSystemWatcher _fileWatcher = new FileSystemWatcher();
_fileWatcher.Path = "C:\\Folder";
_fileWatcher.NotifyFilter = NotifyFilters.LastWrite;
_fileWatcher.Filter = "Version.txt";
_fileWatcher.Changed += new FileSystemEventHandler(OnChanged);
_fileWatcher.EnableRaisingEvents = true;
}
private void OnChanged(object source, FileSystemEventArgs e)
{
.......
}
Dans mon cas, le OnChanged
est appelé deux fois, lorsque je change le fichier texte version.txt
et l'enregistre.
c#
filesystemwatcher
user214707
la source
la source
Réponses:
Je crains que ce soit un bug / fonctionnalité bien connu de la
FileSystemWatcher
classe. Ceci provient de la documentation de la classe:Maintenant, ce morceau de texte concerne l'
Created
événement, mais la même chose s'applique également aux autres événements de fichier. Dans certaines applications, vous pouvez peut-être contourner ce problème en utilisant laNotifyFilter
propriété, mais mon expérience montre que parfois, vous devez également effectuer un filtrage manuel des doublons (hacks).Il y a quelque temps, j'ai réservé une page avec quelques conseils FileSystemWatcher . Vous voudrez peut-être le vérifier.
la source
J'ai "résolu" ce problème en utilisant la stratégie suivante dans mon délégué:
la source
Tous les
OnChanged
événements dupliquésFileSystemWatcher
peuvent être détectés et supprimés en vérifiant l'File.GetLastWriteTime
horodatage du fichier en question. Ainsi:la source
"Rename"
le nom de l'événement qui vous intéresse):Observable.FromEventPattern<FileSystemEventArgs>(fileSystemWatcher, "Renamed") .Select(e => e.EventArgs) .Distinct(e => e.FullPath) .Subscribe(onNext);
DateTime
seule la résolution est en millisecondes, cette méthode fonctionne même si vous la remplacezFile.GetLastWriteTime
parDateTime.Now
. Selon votre situation, vous pouvez également utiliser lea.FullName
dans une variable globale pour détecter les événements en double.Voici ma solution qui m'a aidé à éviter que l'événement ne soit déclenché deux fois:
Ici, j'ai défini la
NotifyFilter
propriété avec uniquement le nom de fichier et la taille.watcher
est mon objet de FileSystemWatcher. J'espère que cela vous aidera.la source
Mon scénario est que j'ai une machine virtuelle avec un serveur Linux dedans. Je développe des fichiers sur l'hôte Windows. Lorsque je change quelque chose dans un dossier sur l'hôte, je veux que toutes les modifications soient téléchargées, synchronisées sur le serveur virtuel via Ftp. Voici comment j'élimine l'événement de modification en double lorsque j'écris dans un fichier (qui marque également le dossier contenant le fichier à modifier):
Je crée principalement une table de hachage pour stocker les informations de temps d'écriture des fichiers. Ensuite, si la table de hachage a le chemin de fichier qui est modifié et sa valeur de temps est la même que la modification du fichier actuellement notifié, alors je sais que c'est le doublon de l'événement et je l'ignore.
la source
ToString("o")
mais soyez prêt pour plus d'échecs.Essayez avec ce code:
la source
if (let==false) { ... } else { let = false; }
? Incroyable comment cela a obtenu des votes positifs, cela ne doit être qu'une question de badges StackOverflow.Voici mon approche:
C'est la solution que j'ai utilisée pour résoudre ce problème sur un projet où j'envoyais le fichier en pièce jointe dans un courrier. Il évitera facilement l'événement déclenché deux fois, même avec un intervalle de temporisation plus petit, mais dans mon cas, 1000 était correct car j'étais plus heureux de manquer quelques modifications que d'inonder la boîte aux lettres avec> 1 message par seconde. Au moins, cela fonctionne très bien au cas où plusieurs fichiers sont modifiés en même temps.
Une autre solution à laquelle j'ai pensé serait de remplacer la liste par un dictionnaire mappant les fichiers vers leur MD5 respectif, de sorte que vous n'auriez pas à choisir un intervalle arbitraire car vous n'auriez pas à supprimer l'entrée mais à mettre à jour sa valeur, et annulez votre contenu s'il n'a pas changé. Il a l'inconvénient d'avoir un dictionnaire croissant en mémoire à mesure que les fichiers sont surveillés et consommant de plus en plus de mémoire, mais j'ai lu quelque part que la quantité de fichiers surveillés dépend du tampon interne du FSW, donc peut-être pas si critique. Ne sais pas non plus comment le temps de calcul MD5 affecterait les performances de votre code, attention = \
la source
lock (_changedFiles) { if (_changedFiles.Contains(e.FullPath)) { return; } _changedFiles.Add(e.FullPath); // add this! } // do your stuff
_changedFiles
accès à partir de plusieurs threads. Une façon de résoudre ce problème consiste à utiliser unConcurrentDictionary
au lieu deList
. Une autre façon consiste à affecter le courantForm
à laTimer.SynchronizingObject
propriété, ainsi qu'à laFileSystemWatcher.SynchronizingObject
propriété.J'ai créé un dépôt Git avec une classe qui s'étend
FileSystemWatcher
pour déclencher les événements uniquement lorsque la copie est effectuée. Il supprime tous les événements modifiés, sauf le dernier, et il ne le déclenche que lorsque le fichier est disponible en lecture.Téléchargez FileSystemSafeWatcher et ajoutez-le à votre projet.
Ensuite, utilisez-le comme un normal
FileSystemWatcher
et surveillez le déclenchement des événements.la source
Je sais que c'est un vieux problème, mais j'ai eu le même problème et aucune des solutions ci-dessus n'a vraiment fait l'affaire pour le problème auquel j'étais confronté. J'ai créé un dictionnaire qui mappe le nom de fichier avec LastWriteTime. Donc, si le fichier n'est pas dans le dictionnaire, poursuivez le processus, vérifiez autrement pour voir quand a été la dernière modification et s'il est différent de ce qu'il est dans le dictionnaire, exécutez le code.
la source
your code here
section, vous devez ajouter ou mettre à jour le dateTimeDictionary.dateTimeDictionary[e.FullPath] = System.IO.File.GetLastWriteTime(e.FullPath);
Un «hack» possible serait de limiter les événements en utilisant des extensions réactives, par exemple:
Dans ce cas, je passe à 50 ms, sur mon système, c'était suffisant, mais des valeurs plus élevées devraient être plus sûres. (Et comme je l'ai dit, c'est toujours un «hack»).
la source
.Distinct(e => e.FullPath)
ce que je trouve beaucoup plus intuitif. Et vous avez restauré le comportement attendu de l'API.J'ai une solution de contournement très rapide et simple ici, cela fonctionne pour moi, et peu importe que l'événement soit déclenché une ou deux fois ou plusieurs fois de temps en temps, vérifiez-le:
la source
Voici une nouvelle solution que vous pouvez essayer. Fonctionne bien pour moi. Dans le gestionnaire d'événements de l'événement modifié, supprimez par programme le gestionnaire du concepteur, affichez un message si vous le souhaitez, puis rajoutez par programme le gestionnaire. exemple:
la source
this.fileSystemWatcher1.Changed -= this.fileSystemWatcher1_Changed;
devrait faire la bonne chose.La raison principale était que le dernier accès du premier événement était l'heure actuelle (écriture de fichier ou heure modifiée). alors le deuxième événement était le dernier temps d'accès d'origine du fichier. Je résous sous le code.
la source
J'ai passé beaucoup de temps à utiliser FileSystemWatcher, et certaines des approches ici ne fonctionneront pas. J'ai vraiment aimé l'approche des événements désactivants, mais malheureusement, cela ne fonctionne pas s'il y a> 1 fichier en cours de suppression, le deuxième fichier sera manqué la plupart sinon tous les temps. J'utilise donc l'approche suivante:
la source
Ce code a fonctionné pour moi.
la source
surtout pour l'avenir moi :)
J'ai écrit un wrapper en utilisant Rx:
Usage:
la source
J'ai changé la façon dont je surveille les fichiers dans les répertoires. Au lieu d'utiliser FileSystemWatcher, j'interroge les emplacements sur un autre thread, puis regarde le LastWriteTime du fichier.
En utilisant ces informations et en gardant un index d'un chemin de fichier et de sa dernière heure d'écriture, je peux déterminer les fichiers qui ont changé ou qui ont été créés dans un emplacement particulier. Cela me retire des bizarreries du FileSystemWatcher. Le principal inconvénient est que vous avez besoin d'une structure de données pour stocker le LastWriteTime et la référence au fichier, mais elle est fiable et facile à implémenter.
la source
Vous pouvez essayer de l'ouvrir pour l'écriture, et en cas de succès, vous pouvez supposer que l'autre application a terminé avec le fichier.
L'ouvrir pour l'écriture ne semble pas déclencher l'événement modifié. Il devrait donc être sûr.
la source
la source
Désolé pour la fouille, mais je lutte contre ce problème depuis un certain temps maintenant et j'ai finalement trouvé un moyen de gérer ces multiples événements déclenchés. Je tiens à remercier tout le monde dans ce fil car je l'ai utilisé dans de nombreuses références lors de la lutte contre ce problème.
Voici mon code complet. Il utilise un dictionnaire pour suivre la date et l'heure de la dernière écriture du fichier. Il compare cette valeur, et si elle est la même, elle supprime les événements. Il définit ensuite la valeur après le démarrage du nouveau thread.
la source
Si ce n'est pas demandé, c'est dommage qu'il n'y ait pas d'échantillons de solution prêts pour F #. Pour résoudre ce problème, voici ma recette, juste parce que je le peux et F # est un merveilleux langage .NET.
Les événements dupliqués sont filtrés à l'aide du
FSharp.Control.Reactive
package, qui n'est qu'un wrapper F # pour les extensions réactives. Tout cela peut être ciblé sur un cadre complet ounetstandard2.0
:la source
Dans mon cas, j'ai besoin d'obtenir la dernière ligne d'un fichier texte inséré par une autre application, dès que l'insertion est terminée. Voici ma solution. Lorsque le premier événement est déclenché, je désactive l'observateur de lever d'autres, puis j'appelle la minuterie TimeElapsedEvent parce que lorsque ma fonction de poignée OnChanged est appelée, j'ai besoin de la taille du fichier texte, mais la taille à ce moment-là n'est pas la taille réelle, c'est la taille du fichier immédiatement avant l'insertion. J'attends donc un moment pour continuer avec la bonne taille de fichier.
la source
Essayez ça, ça marche bien
la source
Je voulais réagir uniquement sur le dernier événement, juste au cas où, également sur un changement de fichier linux, il semblait que le fichier était vide au premier appel, puis rempli à nouveau le suivant et cela ne me dérangeait pas de perdre du temps au cas où le système d'exploitation a décidé de faire un changement de fichier / attribut.
J'utilise .NET async ici pour m'aider à effectuer le filetage.
la source
Je pense que la meilleure solution pour résoudre le problème est d'utiliser des extensions réactives Lorsque vous transformez un événement en observable, vous pouvez simplement ajouter Throttling (..) (initialement appelé Debounce (..))
Exemple de code ici
la source
J'ai pu le faire en ajoutant une fonction qui vérifie les doublons dans un tableau de tampons.
Ensuite, effectuez l'action après que le tableau n'a pas été modifié pour le temps X à l'aide d'une minuterie: - Réinitialisez la minuterie à chaque fois que quelque chose est écrit dans le tampon - Effectuez une action sur tick
Cela intercepte également un autre type de duplication. Si vous modifiez un fichier dans un dossier, le dossier déclenche également un événement Change.
la source
Cette solution a fonctionné pour moi sur l'application de production:
Environnement:
VB.Net Framework 4.5.2
Définissez manuellement les propriétés de l'objet: NotifyFilter = Size
Utilisez ensuite ce code:
la source
Essaye ça!
la source
J'ai dû combiner plusieurs idées des articles ci-dessus et ajouter une vérification de verrouillage de fichier pour que cela fonctionne pour moi:
la source
J'ai abordé le problème de double création comme celui-ci, qui ignore le premier événement:
la source