我有一个应用程序,我正在寻找一个文本文件,如果对文件做了任何更改,我使用OnChanged事件处理程序来处理事件。我正在使用NotifyFilters。LastWriteTime,但是事件仍然被触发两次。这是代码。

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)
{
   .......
}

在我的情况下,OnChanged被调用两次,当我改变文本文件version.txt并保存它。


当前回答

我已经用一个类创建了一个Git repo,该类扩展了FileSystemWatcher,以便仅在复制完成时触发事件。它将丢弃除最后一个事件以外的所有已更改事件,仅在文件可读时才引发该事件。

下载FileSystemSafeWatcher并将其添加到项目中。

然后将其用作普通的FileSystemWatcher并在事件触发时进行监视。

var fsw = new FileSystemSafeWatcher(file);
fsw.EnableRaisingEvents = true;
// Add event handlers here
fsw.Created += fsw_Created;

其他回答

简单地定义一个全局变量var1 = true。

Private Sub FileWatchman_Changed(ByVal sender As System.Object, ByVal e As System.IO.FileSystemEventArgs) Handles FileWatchman.Changed
   If var1 = true 
       your logic goes here
       var1 = false
   Else
       var1 = true 
   End If
End Sub

我的场景是,我有一个虚拟机,其中有一个Linux服务器。我正在Windows主机上开发文件。当我在主机上的一个文件夹中更改某些内容时,我希望所有的更改都被上传,并通过Ftp同步到虚拟服务器上。这是我如何消除重复更改事件当我写入一个文件(这标志着包含文件要修改的文件夹):

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

我主要创建一个哈希表来存储文件写时间信息。然后,如果哈希表有被修改的文件路径,并且它的时间值与当前通知的文件更改相同,那么我就知道它是事件的副本,并忽略它。

这是另一种方法。现在,除了最后一个事件以外,所有事件都被抑制,而不是传播一系列事件中的第一个事件并抑制所有接下来的事件。我认为可以从这种方法中受益的场景更常见。

要做到这一点,我们必须使用滑动延迟。每个传入事件都会取消触发前一个事件的计时器,并重新启动计时器。这开启了一种可能性,即一系列永无止境的事件将永远推迟传播。为了简单起见,在下面的扩展方法中没有针对这种异常情况的规定。

public static class FileSystemWatcherExtensions
{
    public static IDisposable OnAnyEvent(this FileSystemWatcher source,
        WatcherChangeTypes changeTypes, FileSystemEventHandler handler, int delay)
    {
        var cancellations = new Dictionary<string, CancellationTokenSource>(
            StringComparer.OrdinalIgnoreCase);
        var locker = new object();
        if (changeTypes.HasFlag(WatcherChangeTypes.Created))
            source.Created += FileSystemWatcher_Event;
        if (changeTypes.HasFlag(WatcherChangeTypes.Deleted))
            source.Deleted += FileSystemWatcher_Event;
        if (changeTypes.HasFlag(WatcherChangeTypes.Changed))
            source.Changed += FileSystemWatcher_Event;
        if (changeTypes.HasFlag(WatcherChangeTypes.Renamed))
            source.Renamed += FileSystemWatcher_Event;
        return new Disposable(() =>
        {
            source.Created -= FileSystemWatcher_Event;
            source.Deleted -= FileSystemWatcher_Event;
            source.Changed -= FileSystemWatcher_Event;
            source.Renamed -= FileSystemWatcher_Event;
        });

        async void FileSystemWatcher_Event(object sender, FileSystemEventArgs e)
        {
            var key = e.FullPath;
            var cts = new CancellationTokenSource();
            lock (locker)
            {
                if (cancellations.TryGetValue(key, out var existing))
                {
                    existing.Cancel();
                }
                cancellations[key] = cts;
            }
            try
            {
                await Task.Delay(delay, cts.Token);
                // Omitting ConfigureAwait(false) is intentional here.
                // Continuing in the captured context is desirable.
            }
            catch (TaskCanceledException)
            {
                return;
            }
            lock (locker)
            {
                if (cancellations.TryGetValue(key, out var existing)
                    && existing == cts)
                {
                    cancellations.Remove(key);
                }
            }
            cts.Dispose();
            handler(sender, e);
        }
    }

    public static IDisposable OnAllEvents(this FileSystemWatcher source,
        FileSystemEventHandler handler, int delay)
        => OnAnyEvent(source, WatcherChangeTypes.All, handler, delay);

    public static IDisposable OnCreated(this FileSystemWatcher source,
        FileSystemEventHandler handler, int delay)
        => OnAnyEvent(source, WatcherChangeTypes.Created, handler, delay);

    public static IDisposable OnDeleted(this FileSystemWatcher source,
        FileSystemEventHandler handler, int delay)
        => OnAnyEvent(source, WatcherChangeTypes.Deleted, handler, delay);

    public static IDisposable OnChanged(this FileSystemWatcher source,
        FileSystemEventHandler handler, int delay)
        => OnAnyEvent(source, WatcherChangeTypes.Changed, handler, delay);

    public static IDisposable OnRenamed(this FileSystemWatcher source,
        FileSystemEventHandler handler, int delay)
        => OnAnyEvent(source, WatcherChangeTypes.Renamed, handler, delay);

    private struct Disposable : IDisposable
    {
        private readonly Action _action;
        internal Disposable(Action action) => _action = action;
        public void Dispose() => _action?.Invoke();
    }
}

使用的例子:

myWatcher.OnAnyEvent(WatcherChangeTypes.Created | WatcherChangeTypes.Changed,
    MyFileSystemWatcher_Event, 100);

这一行将两个事件(Created和Changed)的订阅组合在一起。所以它大致相当于这些:

myWatcher.Created += MyFileSystemWatcher_Event;
myWatcher.Changed += MyFileSystemWatcher_Event;

不同之处在于,这两个事件被视为单一类型的事件,在这些事件快速连续的情况下,只有最后一个事件将被传播。例如,如果一个Created事件后面跟着两个Changed事件,并且这三个事件之间的时间间隔不超过100 msec,则只有第二个Changed事件将通过调用MyFileSystemWatcher_Event处理程序来传播,而前一个事件将被丢弃。

这个解决方案适用于我的生产应用程序:

环境:

VB。Net Framework 4.5.2

手动设置对象属性:NotifyFilter = Size

然后使用下面的代码:

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

我是这样处理双重创造问题的,忽略了第一个事件:

Private WithEvents fsw As New System.IO.FileSystemWatcher
Private complete As New List(Of String)

Private Sub fsw_Created(ByVal sender As Object, _
    ByVal e As System.IO.FileSystemEventArgs) Handles fsw.Created

    If Not complete.Contains(e.FullPath) Then
        complete.Add(e.FullPath)

    Else
        complete.Remove(e.FullPath)
        Dim th As New Threading.Thread(AddressOf hprocess)
        th.Start(e)

    End If

End Sub