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


当前回答

我花了大量时间使用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.
}

其他回答

我必须结合以上文章中的几个想法,并添加文件锁定检查,让它为我工作:

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

我知道这是一个老问题,但我遇到了同样的问题,上面的解决方案都没有真正解决我所面临的问题。我已经创建了一个字典,它将文件名与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
                }
            }

以下是我的解决方案,帮助我阻止事件被提出两次:

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

这里我只设置了文件名和大小的NotifyFilter属性。 watcher是FileSystemWatcher的对象。希望这能有所帮助。

这已经很晚了,但我最近遇到了这个问题,然后我想发表我的一点贡献。

首先,许多建议的解决方案都适用于单个更新的文件,而我需要在较短的时间内(几十毫秒)收到关于2-3个更改文件的通知,而重复时间相对较长(几十秒到几分钟)。

早期建议的最有趣的链接之一是FileSystemWatcher is a Bit Broken。然而,所提出的解决方案只是部分工作,正如同一作者在。net MemoryCache Expiration Demystified的不稳定行为中指出的那样,即使在20秒后也会发出通知。

然后我所做的是基于类似的原则设计一个愚蠢的替代解决方案,没有MemoryCache。

基本上,它创建了一个List<>的项目,其中有一个Key,它是文件的完整路径和一个过期计时器。如果另一个事件再次触发该更改,则在列表中找到该元素,计时器将更新为新的过期时间。 根据经验,过期时间足够长,足以在单个OnStableChange通知中收集多个事件,而不会太长,以至于感觉没有响应。

当你实例化Whatever时,你也将它链接到一个目录和一个非常基本的外部回调。

没有什么是真正优化的,我只是在几行中寻找一个解决方案。

我把它发表在这里

对我来说,这样你就可以在另一个应用程序上验证 某个更聪明、更有经验的人可以改进它,并帮助我了解它的不足之处

    internal class Whatever
    {
        private FileSystemWatcher? watcher = null;

        public delegate void DelegateFileChange(string path);
        public DelegateFileChange? onChange;

        private const int CacheTimeMilliseconds = 200;

        private class ChangeItem
        {
            public delegate void DelegateChangeItem(string key);
            public string Key { get; set; } = "";
            public System.Timers.Timer Expiration = new();
            public DelegateChangeItem? SignalChanged = null;
        }
        private class ChangeCache
        {
            private readonly List<ChangeItem> _changes = new();

            public void Set(string key, int milliSecs, ChangeItem.DelegateChangeItem? signal = null)
            {
                lock (_changes)
                {
                    ChangeItem? existing = _changes.Find(item => item.Key == key);
                    if (existing != null)
                    {
                        existing.Expiration.Interval = milliSecs;
                        existing.SignalChanged = signal;
                    }
                    else
                    {
                        ChangeItem change = new()
                        {
                            Key = key,
                            SignalChanged = signal
                        };
                        change.Expiration.Interval = milliSecs;
                        change.Expiration.AutoReset = false;
                        change.Expiration.Elapsed += delegate { Change_Elapsed(key); };
                        change.Expiration.Enabled = true;
                        _changes.Add(change);
                    }
                }
            }

            private void Change_Elapsed(string key)
            {
                lock (_changes)
                {
                    ChangeItem? existing = _changes.Find(item => item.Key == key);
                    existing?.SignalChanged?.Invoke(key);
                    _changes.RemoveAll(item => item.Key == key);
                }
            }
        }

        private ChangeCache changeCache = new();

        public bool Link(string directory, DelegateFileChange? fileChange = null)
        {
            bool result = false;

            try
            {
                if (Directory.Exists(directory))
                {
                    watcher = new FileSystemWatcher(directory);
                    watcher.NotifyFilter = NotifyFilters.LastWrite;
                    watcher.Changed += Watcher_Changed;

                    onChange = fileChange;

                    watcher.Filter = "*.*";
                    watcher.IncludeSubdirectories = true;
                    watcher.EnableRaisingEvents = true;

                    result = true;
                }
            }
            catch (Exception)
            {
            }

            return result;
        }

        private void OnStableChange(string path)
        {
            if (File.Exists(path))
            {
                onChange?.Invoke(path);
            }
        }

        public void Watcher_Changed(object sender, FileSystemEventArgs e)
        {
            changeCache.Set(e.FullPath, CacheTimeMilliseconds, OnStableChange);
        }
    }

如果你注册了OnChanged事件,那么在修改之前删除被监视的文件可能会起作用,只要你只需要监视OnChange事件。