FileSystemWatcher Changed事件被引发两次

问题描述 投票:306回答:35

我有一个应用程序,我正在寻找一个文本文件,如果对该文件进行任何更改,我使用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并保存它。

c# filesystemwatcher
35个回答
269
投票

我担心这是FileSystemWatcher类的一个众所周知的bug /功能。这来自该课程的文档:

在某些情况下,您可能会注意到单个创建事件会生成由组件处理的多个Created事件。例如,如果使用FileSystemWatcher组件来监视目录中新文件的创建,然后使用记事本创建文件进行测试,即使只创建了一个文件,也可能会看到生成两个Created事件。这是因为Notepad在写入过程中执行多个文件系统操作。记事本批量写入磁盘,创建文件的内容,然后创建文件属性。其他应用程序可以以相同的方式执行。由于FileSystemWatcher监视操作系统活动,因此将拾取这些应用程序触发的所有事件。

现在这篇文章是关于Created事件的,但同样的事情也适用于其他文件事件。在某些应用程序中,您可以通过使用NotifyFilter属性来解决这个问题,但我的经验表明,有时您还必须进行一些手动重复过滤(hacks)。

前段时间我用一些FileSystemWatcher tips书签了一个页面。你可能想看一下。


3
投票

我在这里有一个非常快速和简单的解决方法,它确实适用于我,并且无论事件偶尔会被触发一次或两次或更多次,请查看:

private int fireCount = 0;
private void inputFileWatcher_Changed(object sender, FileSystemEventArgs e)
    {
       fireCount++;
       if (fireCount == 1)
        {
            MessageBox.Show("Fired only once!!");
            dowork();
        }
        else
        {
            fireCount = 0;
        }
    }
}

3
投票

我创建了一个Git仓库,其中包含一个扩展FileSystemWatcher的类,只有在复制完成后才能触发事件。它会丢弃所有已更改的事件,只有当文件可供读取时才会引发它。

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

然后将其用作正常的FileSystemWatcher并在事件触发时进行监控。

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

3
投票

这是您可以尝试的新解决方案。对我来说效果很好。在已更改事件的事件处理程序中,以编程方式从设计器输出中删除处理程序,如果需要,则以编程方式添加处理程序。例:

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

2
投票

主要原因是第一个事件的最后访问时间是当前时间(文件写入或更改时间)。然后第二个事件是文件的原始最后访问时间。我在代码下解决。

        var lastRead = DateTime.MinValue;

        Watcher = new FileSystemWatcher(...)
        {
            NotifyFilter = NotifyFilters.FileName | NotifyFilters.LastWrite,
            Filter = "*.dll",
            IncludeSubdirectories = false,
        };
        Watcher.Changed += (senderObject, ea) =>
        {
            var now = DateTime.Now;
            var lastWriteTime = File.GetLastWriteTime(ea.FullPath);

            if (now == lastWriteTime)
            {
                return;
            }

            if (lastWriteTime != lastRead)
            {
                // do something...
                lastRead = lastWriteTime;
            }
        };

        Watcher.EnableRaisingEvents = true;

2
投票

我花了大量时间使用FileSystemWatcher,这里的一些方法不起作用。我真的很喜欢禁用事件方法,但不幸的是,如果有1个文件被丢弃,它就无法工作,如果不是所有时间都会丢失第二个文件。所以我使用以下方法:

private void EventCallback(object sender, FileSystemEventArgs e)
{
    var fileName = e.FullPath;

    if (!File.Exists(fileName))
    {
        // We've dealt with the file, this is just supressing further events.
        return;
    }

    // File exists, so move it to a working directory. 
    File.Move(fileName, [working directory]);

    // Kick-off whatever processing is required.
}

2
投票

这段代码对我有用。

        private void OnChanged(object source, FileSystemEventArgs e)
    {

        string fullFilePath = e.FullPath.ToString();
        string fullURL = buildTheUrlFromStudyXML(fullFilePath);

        System.Diagnostics.Process.Start("iexplore", fullURL);

        Timer timer = new Timer();
        ((FileSystemWatcher)source).Changed -= new FileSystemEventHandler(OnChanged);
        timer.Interval = 1000;
        timer.Elapsed += new ElapsedEventHandler(t_Elapsed);
        timer.Start();
    }

    private void t_Elapsed(object sender, ElapsedEventArgs e)
    {
        ((Timer)sender).Stop();
        theWatcher.Changed += new FileSystemEventHandler(OnChanged);
    }

2
投票

主要是为了将来我:)

我用Rx编写了一个包装器:

 public class WatcherWrapper : IDisposable
{
    private readonly FileSystemWatcher _fileWatcher;
    private readonly Subject<FileSystemEventArgs> _infoSubject;
    private Subject<FileSystemEventArgs> _eventSubject;

    public WatcherWrapper(string path, string nameFilter = "*.*", NotifyFilters? notifyFilters = null)
    {
        _fileWatcher = new FileSystemWatcher(path, nameFilter);

        if (notifyFilters != null)
        {
            _fileWatcher.NotifyFilter = notifyFilters.Value;
        }

        _infoSubject = new Subject<FileSystemEventArgs>();
        _eventSubject = new Subject<FileSystemEventArgs>();

        Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Changed").Select(e => e.EventArgs)
            .Subscribe(_infoSubject.OnNext);
        Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Created").Select(e => e.EventArgs)
            .Subscribe(_infoSubject.OnNext);
        Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Deleted").Select(e => e.EventArgs)
            .Subscribe(_infoSubject.OnNext);
        Observable.FromEventPattern<FileSystemEventArgs>(_fileWatcher, "Renamed").Select(e => e.EventArgs)
            .Subscribe(_infoSubject.OnNext);

        // this takes care of double events and still works with changing the name of the same file after a while
        _infoSubject.Buffer(TimeSpan.FromMilliseconds(20))
            .Select(x => x.GroupBy(z => z.FullPath).Select(z => z.LastOrDefault()).Subscribe(
                infos =>
                {
                    if (infos != null)
                        foreach (var info in infos)
                        {
                            {
                                _eventSubject.OnNext(info);
                            }
                        }
                });

        _fileWatcher.EnableRaisingEvents = true;
    }

    public IObservable<FileSystemEventArgs> FileEvents => _eventSubject;


    public void Dispose()
    {
        _fileWatcher?.Dispose();
        _eventSubject.Dispose();
        _infoSubject.Dispose();
    }
}

用法:

var watcher = new WatcherWrapper(_path, "*.info");
// all more complicated and scenario specific filtering of events can be done here    
watcher.FileEvents.Where(x => x.ChangeType != WatcherChangeTypes.Deleted).Subscribe(x => //do stuff)

1
投票

我改变了监视目录中文件的方式。而不是使用FileSystemWatcher我在另一个线程上轮询位置,然后查看文件的LastWriteTime。

DateTime lastWriteTime = File.GetLastWriteTime(someFilePath);

使用此信息并保留文件路径的索引及其最新写入时间,我可以确定已更改的文件或已在特定位置创建的文件。这使我免于FileSystemWatcher的奇怪之处。主要缺点是您需要一个数据结构来存储LastWriteTime和对文件的引用,但它是可靠且易于实现的。


1
投票

您可以尝试打开它进行写入,如果成功,则可以假设其他应用程序已完成该文件。

private void OnChanged(object source, FileSystemEventArgs e)
{
    try
    {
        using (var fs = File.OpenWrite(e.FullPath))
        {
        }
        //do your stuff
    }
    catch (Exception)
    {
        //no write access, other app not done
    }
}

只是打开它进行写入似乎不会引发更改的事件。所以它应该是安全的。


1
投票
FileReadTime = DateTime.Now;

private void File_Changed(object sender, FileSystemEventArgs e)
{            
    var lastWriteTime = File.GetLastWriteTime(e.FullPath);
    if (lastWriteTime.Subtract(FileReadTime).Ticks > 0)
    {
        // code
        FileReadTime = DateTime.Now;
    }
}

143
投票

我在我的委托中使用以下策略“修复”了这个问题:

// fsw_ is the FileSystemWatcher instance used by my application.

private void OnDirectoryChanged(...)
{
   try
   {
      fsw_.EnableRaisingEvents = false;

      /* do my stuff once asynchronously */
   }

   finally
   {
      fsw_.EnableRaisingEvents = true;
   }
}

1
投票

很抱歉这个问题很严重,但我一直在与这个问题作斗争一段时间,最后想出办法处理这些多次被解雇的事件。我要感谢这个主题中的每个人,因为我在讨论这个问题时已经在许多参考文献中使用过它。

这是我的完整代码。它使用字典来跟踪上次写入文件的日期和时间。它会比较该值,如果相同,则会抑制事件。然后在启动新线程后设置该值。

using System.Threading; // used for backgroundworker
using System.Diagnostics; // used for file information
private static IDictionary<string, string> fileModifiedTable = new Dictionary<string, string>(); // used to keep track of our changed events

private void fswFileWatch_Changed( object sender, FileSystemEventArgs e )
    {
        try
        {
           //check if we already have this value in our dictionary.
            if ( fileModifiedTable.TryGetValue( e.FullPath, out sEmpty ) )
            {              
                //compare timestamps      
                if ( fileModifiedTable[ e.FullPath ] != File.GetLastWriteTime( e.FullPath ).ToString() )
                {        
                    //lock the table                
                    lock ( fileModifiedTable )
                    {
                        //make sure our file is still valid
                        if ( File.Exists( e.FullPath ) )
                        {                               
                            // create a new background worker to do our task while the main thread stays awake. Also give it do work and work completed handlers
                            BackgroundWorker newThreadWork = new BackgroundWorker();
                            newThreadWork.DoWork += new DoWorkEventHandler( bgwNewThread_DoWork );
                            newThreadWork.RunWorkerCompleted += new RunWorkerCompletedEventHandler( bgwNewThread_RunWorkerCompleted );

                            // capture the path
                            string eventFilePath = e.FullPath;
                            List<object> arguments = new List<object>();

                            // add arguments to pass to the background worker
                            arguments.Add( eventFilePath );
                            arguments.Add( newEvent.File_Modified );

                            // start the new thread with the arguments
                            newThreadWork.RunWorkerAsync( arguments );

                            fileModifiedTable[ e.FullPath ] = File.GetLastWriteTime( e.FullPath ).ToString(); //update the modified table with the new timestamp of the file.
                            FILE_MODIFIED_FLAG.WaitOne(); // wait for the modified thread to complete before firing the next thread in the event multiple threads are being worked on.
                        }
                    }
                }
            }
        }
        catch ( IOException IOExcept )
        {
            //catch any errors
            postError( IOExcept, "fswFileWatch_Changed" );
        }
    }

1
投票

事件如果没有被问到,很遗憾F#没有现成的解决方案样本。解决这个问题的方法是我的食谱,因为我能和F#是一种很棒的.NET语言。

使用FSharp.Control.Reactive包过滤掉重复的事件,netstandard2.0包只是反应式扩展的F#包装器。所有可以针对完整框架或let createWatcher path filter () = new FileSystemWatcher( Path = path, Filter = filter, EnableRaisingEvents = true, SynchronizingObject = null // not needed for console applications ) let createSources (fsWatcher: FileSystemWatcher) = // use here needed events only. // convert `Error` and `Renamed` events to be merded [| fsWatcher.Changed :> IObservable<_> fsWatcher.Deleted :> IObservable<_> fsWatcher.Created :> IObservable<_> //fsWatcher.Renamed |> Observable.map renamedToNeeded //fsWatcher.Error |> Observable.map errorToNeeded |] |> Observable.mergeArray let handle (e: FileSystemEventArgs) = printfn "handle %A event '%s' '%s' " e.ChangeType e.Name e.FullPath let watch path filter throttleTime = // disposes watcher if observer subscription is disposed Observable.using (createWatcher path filter) createSources // filter out multiple equal events |> Observable.distinctUntilChanged // filter out multiple Changed |> Observable.throttle throttleTime |> Observable.subscribe handle [<EntryPoint>] let main _args = let path = @"C:\Temp\WatchDir" let filter = "*.zip" let throttleTime = TimeSpan.FromSeconds 10. use _subscription = watch path filter throttleTime System.Console.ReadKey() |> ignore 0 // return an integer exit code

  private static readonly FileSystemWatcher Watcher = new FileSystemWatcher();
    static void Main(string[] args)
    {
        Console.WriteLine("Watching....");

        Watcher.Path = @"D:\Temp\Watcher";
        Watcher.Changed += OnChanged;
        Watcher.EnableRaisingEvents = true;
        Console.ReadKey();
    }

    static void OnChanged(object sender, FileSystemEventArgs e)
    {
        try
        {
            Watcher.Changed -= OnChanged;
            Watcher.EnableRaisingEvents = false;
            Console.WriteLine($"File Changed. Name: {e.Name}");
        }
        catch (Exception exception)
        {
            Console.WriteLine(exception);
        }
        finally
        {
            Watcher.Changed += OnChanged;
            Watcher.EnableRaisingEvents = true;
        }
    }

1
投票

试试这个,它工作正常

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

1
投票

我想只对最后一个事件作出反应,以防万一,也是在一个linux文件上更改它似乎在第一次调用时文件是空的然后再次填充下一个并且不介意丢失一些时间以防万一决定做一些文件/属性更改。

我在这里使用.NET异步来帮助我进行线程化。

Function is_duplicate(str1 As String) As Boolean
    If lb_actions_list.Items.Count = 0 Then
        Return False
    Else
        Dim compStr As String = lb_actions_list.Items(lb_actions_list.Items.Count - 1).ToString
        compStr = compStr.Substring(compStr.IndexOf("-") + 1).Trim

        If compStr <> str1 AndAlso compStr.parentDir <> str1 & "\" Then
            Return False
        Else
            Return True
        End If
    End If
End Function

Public Module extentions
<Extension()>
Public Function parentDir(ByVal aString As String) As String
    Return aString.Substring(0, CInt(InStrRev(aString, "\", aString.Length - 1)))
End Function
End Module

0
投票

我能够通过添加一个检查缓冲区数组中重复项的函数来做到这一点。

然后使用计时器在数组未被修改X时间后执行操作: - 每次将某些内容写入缓冲区时重置计时器 - 执行tick时的操作

这也捕获了另一种重复类型。如果修改文件夹中的文件,该文件夹也会抛出Change事件。

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
投票

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

环境:

VB.Net Framework 4.5.2

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

然后使用此代码:

string temp="";

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)
{
   .......
if(temp=="")
{
   //do thing you want.
   temp = e.name //name of text file.
}else if(temp !="" && temp != e.name)
{
   //do thing you want.
   temp = e.name //name of text file.
}else
{
  //second fire ignored.
}

}

0
投票

试试这个!

FileSystemWatcher fileSystemWatcher;

private void DirectoryWatcher_Start()
{
    FileSystemWatcher fileSystemWatcher = new FileSystemWatcher
    {
        Path = @"c:\mypath",
        NotifyFilter = NotifyFilters.LastWrite,
        Filter = "*.*",
        EnableRaisingEvents = true
    };

    fileSystemWatcher.Changed += new FileSystemEventHandler(DirectoryWatcher_OnChanged);
}

private static void WaitUntilFileIsUnlocked(String fullPath, Action<String> callback, FileAccess fileAccess = FileAccess.Read, Int32 timeoutMS = 10000)
{
    Int32 waitMS = 250;
    Int32 currentMS = 0;
    FileInfo file = new FileInfo(fullPath);
    FileStream stream = null;
    do
    {
        try
        {
            stream = file.Open(FileMode.Open, fileAccess, FileShare.None);
            stream.Close();
            callback(fullPath);
            return;
        }
        catch (IOException)
        {
        }
        finally
        {
            if (stream != null)
                stream.Dispose();
        }
        Thread.Sleep(waitMS);
        currentMS += waitMS;
    } while (currentMS < timeoutMS);
}    

private static Dictionary<String, DateTime> DirectoryWatcher_fileLastWriteTimeCache = new Dictionary<String, DateTime>();

private void DirectoryWatcher_OnChanged(Object source, FileSystemEventArgs ev)
{
    try
    {
        lock (DirectoryWatcher_fileLastWriteTimeCache)
        {
            DateTime lastWriteTime = File.GetLastWriteTime(ev.FullPath);
            if (DirectoryWatcher_fileLastWriteTimeCache.ContainsKey(ev.FullPath))
            {
                if (DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath].AddMilliseconds(500) >= lastWriteTime)
                    return;     // file was already handled
            }

            DirectoryWatcher_fileLastWriteTimeCache[ev.FullPath] = lastWriteTime;
        }

        Task.Run(() => WaitUntilFileIsUnlocked(ev.FullPath, fullPath =>
        {
            // do the job with fullPath...
        }));

    }
    catch (Exception e)
    {
        // handle exception
    }
}

0
投票

我必须结合上面帖子中的几个想法并添加文件锁定检查以使其适用于我:

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

0
投票

我接近了像这样的双重创建问题,忽略了第一个事件:

private void OnChanged(object sender, FilesystemEventArgs e)
{
    if (FSWatcher.IncludeSubdirectories == true)
    {
        if (File.Exists(e.FullPath)) { DO YOUR FILE CHANGE STUFF HERE... }
    }
    else DO YOUR DIRECTORY CHANGE STUFF HERE...
}

0
投票

很多这些答案都令人震惊,真的。下面是我的XanderUI控件库中的一些代码修复了这个问题。

 private void OnChanged(object source, FileSystemEventArgs e)
    {
        string sTabName = Path.GetFileNameWithoutExtension(e.Name);
        string sLastLine = ReadLastLine(e.FullPath);
        if(sLastLine != _dupeCheck)
        {
            TabPage tp = tcLogs.TabPages[sTabName];
            TextBox tbLog = (TextBox)tp.Controls[0] as TextBox;

            tbLog.Invoke(new Action(() => tbLog.AppendText(sLastLine + Environment.NewLine)));
            tbLog.Invoke(new Action(() => tbLog.SelectionStart = tbLog.Text.Length));
            tbLog.Invoke(new Action(() => tbLog.ScrollToCaret()));
            _dupeCheck = sLastLine;
        }
    }

    public static String ReadLastLine(string path)
    {
        return ReadLastLine(path, Encoding.Default, "\n");
    }

    public static String ReadLastLine(string path, Encoding encoding, string newline)
    {
        int charsize = encoding.GetByteCount("\n");
        byte[] buffer = encoding.GetBytes(newline);
        using (FileStream stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
        {
            long endpos = stream.Length / charsize;
            for (long pos = charsize; pos < endpos; pos += charsize)
            {
                stream.Seek(-pos, SeekOrigin.End);
                stream.Read(buffer, 0, buffer.Length);
                if (encoding.GetString(buffer) == newline)
                {
                    buffer = new byte[stream.Length - stream.Position];
                    stream.Read(buffer, 0, buffer.Length);
                    return encoding.GetString(buffer);
                }
            }
        }
        return null;
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto)]
    private static extern int SendMessage(IntPtr hWnd, int wMsg, IntPtr wParam, IntPtr lParam);

    private const int WM_VSCROLL = 0x115;
    private const int SB_BOTTOM = 7;

    /// <summary>
    /// Scrolls the vertical scroll bar of a multi-line text box to the bottom.
    /// </summary>
    /// <param name="tb">The text box to scroll</param>
    public static void ScrollToBottom(TextBox tb)
    {
        SendMessage(tb.Handle, WM_VSCROLL, (IntPtr)SB_BOTTOM, IntPtr.Zero);
    }

103
投票

通过检查相关文件上的OnChanged时间戳,可以检测并丢弃来自FileSystemWatcher的任何重复的File.GetLastWriteTime事件。像这样:

DateTime lastRead = DateTime.MinValue;

void OnChanged(object source, FileSystemEventArgs a)
{
    DateTime lastWriteTime = File.GetLastWriteTime(uri);
    if (lastWriteTime != lastRead)
    {
        doStuff();
        lastRead = lastWriteTime;
    }
    // else discard the (duplicated) OnChanged event
}

0
投票

我简单地添加一个欺骗检查如下:

qazxswpoi

21
投票

这是我的解决方案,帮助我阻止事件被提升两次:

watcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size;

在这里,我将NotifyFilter属性设置为仅具有文件名和大小。 watcher是FileSystemWatcher的对象。希望这会有所帮助。


8
投票

这是我的方法:

// 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();
}

这是我在一个项目中解决此问题的解决方案,我将该文件作为附件发送到邮件中。即使定时器间隔较小,它也可以轻松避免两次触发事件,但在我的情况下,1000是好的,因为我很幸福,因为我没有比每次输入> 1条消息的邮箱更少的更改。至少它可以正常工作,以防几个文件在同一时间更改。

我想到的另一个解决方案是用字典映射文件替换列表到它们各自的MD5,因此您不必选择任意间隔,因为您不必删除条目但更新其值,并且如果没有改变,取消你的东西。它有一个缺点,即字典在内存中增长,因为文件被监控并占用越来越多的内存,但我已经读到了某个地方,监控的文件数量取决于FSW的内部缓冲区,所以可能不那么重要。不知道MD5的计算时间如何影响你的代码的表现,小心=


7
投票

我的方案是我有一台带有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;
    }
}

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


6
投票

试试这段代码:

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

4
投票

我知道这是一个老问题,但有同样的问题,上述解决方案都没有真正解决我面临的问题。我创建了一个字典,用于将文件名映射到LastWriteTime。因此,如果文件不在字典中,则继续进行其他方式检查以查看最后修改时间的时间,以及是否与字典中的内容不同运行代码。

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

3
投票

一个可能的“黑客”是使用Reactive Extensions来限制事件,例如:

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

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

watcher.EnableRaisingEvents = true;

在这种情况下,我限制在50ms,在我的系统上已经足够了,但更高的值应该更安全。 (就像我说的,它仍然是'黑客')。

© www.soinside.com 2019 - 2024. All rights reserved.