FileSystemWatcher Changed event is raised twice

前端 未结 30 3292
死守一世寂寞
死守一世寂寞 2020-11-22 06:01

I have an application where I am looking for a text file and if there are any changes made to the file I am using the OnChanged eventhandler to handle the event

相关标签:
30条回答
  • 2020-11-22 06:31

    I think the best solution to solve the issue is to use reactive extensions When you transform event into observable, then you can just add Throttling(..) (originally called Debounce(..))

    Sample code here

            var templatesWatcher = new FileSystemWatcher(settingsSnapshot.Value.TemplatesDirectory)
            {
                NotifyFilter = NotifyFilters.LastWrite,
                IncludeSubdirectories = true
            };
    
            templatesWatcher.EnableRaisingEvents = true;
    
            Observable.FromEventPattern<FileSystemEventHandler, FileSystemEventArgs>(
                    addHandler => templatesWatcher.Changed += addHandler,
                    removeHandler => templatesWatcher.Changed -= removeHandler)
                .Throttle(TimeSpan.FromSeconds(5))
                .Subscribe(args =>
                {
                    _logger.LogInformation($"Template file {args.EventArgs.Name} has changed");
                    //TODO do something
                });
    
    0 讨论(0)
  • 2020-11-22 06:32

    I have changed the way I monitor files in directories. Instead of using the FileSystemWatcher I poll locations on another thread and then look at the LastWriteTime of the file.

    DateTime lastWriteTime = File.GetLastWriteTime(someFilePath);
    

    Using this information and keeping an index of a file path and it's latest write time I can determine files that have changed or that have been created in a particular location. This removes me from the oddities of the FileSystemWatcher. The main downside is that you need a data structure to store the LastWriteTime and the reference to the file, but it is reliable and easy to implement.

    0 讨论(0)
  • 2020-11-22 06:32

    This solution worked for me on production application:

    Environment:

    VB.Net Framework 4.5.2

    Set manually object properties: NotifyFilter = Size

    Then use this code:

    Public Class main
        Dim CalledOnce = False
        Private Sub FileSystemWatcher1_Changed(sender As Object, e As IO.FileSystemEventArgs) Handles FileSystemWatcher1.Changed
                If (CalledOnce = False) Then
                    CalledOnce = True
                    If (e.ChangeType = 4) Then
                        ' Do task...
                    CalledOnce = False
                End If
            End Sub
    End Sub
    
    0 讨论(0)
  • 2020-11-22 06:35

    One possible 'hack' would be to throttle the events using Reactive Extensions for example:

    var watcher = new FileSystemWatcher("./");
    
    Observable.FromEventPattern<FileSystemEventArgs>(watcher, "Changed")
                .Throttle(new TimeSpan(500000))
                .Subscribe(HandleChangeEvent);
    
    watcher.EnableRaisingEvents = true;
    

    In this case I'm throttling to 50ms, on my system that was enough, but higher values should be safer. (And like I said, it's still a 'hack').

    0 讨论(0)
  • 2020-11-22 06:36

    Here is a new solution you can try. Works well for me. In the event handler for the changed event programmatically remove the handler from the designer output a message if desired then programmatically add the handler back. example:

    public void fileSystemWatcher1_Changed( object sender, System.IO.FileSystemEventArgs e )
        {            
            fileSystemWatcher1.Changed -= new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
            MessageBox.Show( "File has been uploaded to destination", "Success!" );
            fileSystemWatcher1.Changed += new System.IO.FileSystemEventHandler( fileSystemWatcher1_Changed );
        }
    
    0 讨论(0)
  • 2020-11-22 06:37

    I wanted to react only on the last event, just in case, also on a linux file change it seemed that the file was empty on the first call and then filled again on the next and did not mind loosing some time just in case the OS decided to do some file/attribute change.

    I am using .NET async here to help me do the threading.

        private static int _fileSystemWatcherCounts;
        private async void OnChanged(object sender, FileSystemEventArgs e)
        {
            // Filter several calls in short period of time
            Interlocked.Increment(ref _fileSystemWatcherCounts);
            await Task.Delay(100);
            if (Interlocked.Decrement(ref _fileSystemWatcherCounts) == 0)
                DoYourWork();
        }
    
    0 讨论(0)
提交回复
热议问题