392 Stimmen

FileSystemWatcher Changed-Ereignis wird zweimal ausgelöst

Ich habe eine Anwendung, in der ich nach einer Textdatei suche, und wenn Änderungen an der Datei vorgenommen wurden, verwende ich die OnChanged eventhandler, um das Ereignis zu behandeln. Ich verwende die NotifyFilters.LastWriteTime aber trotzdem wird das Ereignis zweimal ausgelöst. Hier ist der 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)
{
   .......
}

In meinem Fall ist die OnChanged wird zweimal aufgerufen, wenn ich die Textdatei ändere version.txt und speichern Sie es.

11voto

Rémy Esmery Punkte 558

Hier ist mein Ansatz:

// Consider having a List<String> named _changedFiles

private void OnChanged(object source, FileSystemEventArgs e)
{
    lock (_changedFiles)
    {
        if (_changedFiles.Contains(e.FullPath))
        {
            return;
        }
        _changedFiles.Add(e.FullPath);
    }

    // do your stuff

    System.Timers.Timer timer = new Timer(1000) { AutoReset = false };
    timer.Elapsed += (timerElapsedSender, timerElapsedArgs) =>
    {
        lock (_changedFiles)
        {
            _changedFiles.Remove(e.FullPath);
        }
    };
   timer.Start();
}

Mit dieser Lösung habe ich das Problem bei einem Projekt gelöst, bei dem ich die Datei als Anhang in einer E-Mail versendet habe. Auch mit einem kleineren Zeitintervall lässt sich das zweimalige Auslösen des Ereignisses leicht vermeiden, aber in meinem Fall war 1000 in Ordnung, da ich lieber ein paar Änderungen verpasste als das Postfach mit > 1 Nachricht pro Sekunde zu überfluten. Zumindest funktioniert es gut, wenn mehrere Dateien zur gleichen Zeit geändert werden.

Eine andere Lösung, die ich mir überlegt habe, wäre, die Liste durch ein Wörterbuch zu ersetzen, das die Dateien ihren jeweiligen MD5-Werten zuordnet, so dass man kein willkürliches Intervall wählen müsste, da man den Eintrag nicht löschen, sondern seinen Wert aktualisieren müsste, und seine Arbeit abbrechen könnte, wenn er sich nicht geändert hat. Das hat den Nachteil, dass das Wörterbuch mit der Anzahl der überwachten Dateien wächst und immer mehr Speicher verbraucht, aber ich habe irgendwo gelesen, dass die Anzahl der überwachten Dateien vom internen Puffer des FSW abhängt, also vielleicht nicht so kritisch. Ich weiß nicht, wie sich die MD5-Berechnungszeit auf die Leistung deines Codes auswirkt, aber sei vorsichtig =\

9voto

Ikon Punkte 643

Mein Szenario ist, dass ich eine virtuelle Maschine mit einem Linux-Server darin habe. Ich entwickle Dateien auf dem Windows-Host. Wenn ich etwas in einem Ordner auf dem Host ändere, möchte ich, dass alle Änderungen hochgeladen und über Ftp mit dem virtuellen Server synchronisiert werden. So verhindere ich, dass beim Schreiben in eine Datei ein doppeltes Änderungsereignis auftritt (wodurch der Ordner, der die zu ändernde Datei enthält, ebenfalls markiert wird):

private Hashtable fileWriteTime = new Hashtable();

private void fsw_sync_Changed(object source, FileSystemEventArgs e)
{
    string path = e.FullPath.ToString();
    string currentLastWriteTime = File.GetLastWriteTime( e.FullPath ).ToString();

    // if there is no path info stored yet
    // or stored path has different time of write then the one now is inspected
    if ( !fileWriteTime.ContainsKey(path) ||
         fileWriteTime[path].ToString() != currentLastWriteTime
    )
    {
        //then we do the main thing
        log( "A CHANGE has occured with " + path );

        //lastly we update the last write time in the hashtable
        fileWriteTime[path] = currentLastWriteTime;
    }
}

Hauptsächlich erstelle ich eine Hashtabelle, um Informationen über die Schreibzeit von Dateien zu speichern. Wenn die Hashtabelle dann den geänderten Dateipfad enthält und der Zeitwert mit der Änderung der aktuell gemeldeten Datei übereinstimmt, weiß ich, dass es sich um das Duplikat des Ereignisses handelt und ignoriere es.

7voto

Jamie Krcmar Punkte 119

Versuchen Sie es mit diesem Code:

class WatchPlotDirectory
{
    bool let = false;
    FileSystemWatcher watcher;
    string path = "C:/Users/jamie/OneDrive/Pictures/Screenshots";

    public WatchPlotDirectory()
    {
        watcher = new FileSystemWatcher();
        watcher.Path = path;
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite
                               | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        watcher.Filter = "*.*";
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        watcher.Renamed += new RenamedEventHandler(OnRenamed);
        watcher.EnableRaisingEvents = true;
    }

    void OnChanged(object sender, FileSystemEventArgs e)
    {
        if (let==false) {
            string mgs = string.Format("File {0} | {1}",
                                       e.FullPath, e.ChangeType);
            Console.WriteLine("onchange: " + mgs);
            let = true;
        }

        else
        {
            let = false;
        }

    }

    void OnRenamed(object sender, RenamedEventArgs e)
    {
        string log = string.Format("{0} | Renamed from {1}",
                                   e.FullPath, e.OldName);
        Console.WriteLine("onrenamed: " + log);

    }

    public void setPath(string path)
    {
        this.path = path;
    }
}

5voto

Zardaloop Punkte 1508

Ich weiß, dass dies ein altes Problem ist, aber ich hatte das gleiche Problem und keine der oben genannten Lösungen hat das Problem gelöst, mit dem ich konfrontiert war. Ich habe ein Wörterbuch erstellt, das den Dateinamen mit der LastWriteTime verknüpft. Wenn die Datei also nicht im Wörterbuch enthalten ist, wird der Prozess auf andere Weise fortgesetzt, um zu sehen, wann die letzte Änderungszeit war, und wenn sie sich von der im Wörterbuch enthaltenen unterscheidet, wird der Code ausgeführt.

    Dictionary<string, DateTime> dateTimeDictionary = new Dictionary<string, DateTime>(); 

        private void OnChanged(object source, FileSystemEventArgs e)
            {
                if (!dateTimeDictionary.ContainsKey(e.FullPath) || (dateTimeDictionary.ContainsKey(e.FullPath) && System.IO.File.GetLastWriteTime(e.FullPath) != dateTimeDictionary[e.FullPath]))
                {
                    dateTimeDictionary[e.FullPath] = System.IO.File.GetLastWriteTime(e.FullPath);

                    //your code here
                }
            }

4voto

TimothyP Punkte 19922

Ein möglicher "Hack" wäre z. B. die Drosselung der Ereignisse mithilfe von Reactive Extensions:

var watcher = new FileSystemWatcher("./");

Observable.FromEventPattern<FileSystemEventArgs>(watcher, "Changed")
            .Throttle(new TimeSpan(500000))
            .Subscribe(HandleChangeEvent);

watcher.EnableRaisingEvents = true;

In diesem Fall drossle ich auf 50ms, auf meinem System war das genug, aber höhere Werte sollten sicherer sein. (Und wie ich schon sagte, es ist immer noch ein "Hack").

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X