FileSystemWatcher:忽略对文件的第一次更改

问题描述 投票:1回答:4

我在FileSystemWatcher中使用C#类来跟踪文件在修改文件后保存文件时的更改。

FileSystemWatcher watcher = new FileSystemWatcher()
{
    Path = DIR_NAME,
    NotifyFilter = NotifyFilters.LastWrite | 
                   NotifyFilters.CreationTime | 
                   NotifyFilters.LastAccess,
    Filter = "*.pdf",
    IncludeSubdirectories = true,
    EnableRaisingEvents = true
};

watcher.Changed += OnChanged;

但是,我想要跟踪的文件是以编程方式创建的,如下所示:

FileStream stream = FileUtil.CreateNewFile(filePath); // Creates a file
file.WriteFileToStream(stream); // Writes into the file

理想情况下,我的代码应该运行如下:

  1. 该程序将创建一个文件并将一些内容写入其中。
  2. 用户打开文件,修改并保存。
  3. 此时,它应该触发OnChanged,即我希望我的OnChanged处理程序代码仅在真实用户修改并保存时执行。

但是,只要文件以编程方式写入,即在以下行中,它就会被触发:

file.WriteFileToStream(stream);

从技术上讲,这是正确的行为,因为它跟踪文件中的更改。但是,我的业务案例不允许在最初创建和写入文件时执行OnChanged处理程序代码。

我的问题是,在创建文件并以编程方式第一次写入时,是否有一种解决方法可以跳过OnChanged调用?

注意:

  1. 应用程序体系结构要求在我的应用程序启动时初始化FileSystemWatcher。所以我在创建文件后无法注册它。
  2. 这是一个多用户应用程序,其中多个用户将同时写入文件,因此我无法在创建文件之前禁用观察程序并在创建文件后启用它:

watcher.EnableRaisingEvents = false; 
CreateFile();
watcher.EnableRaisingEvents = true;
c# filesystemwatcher
4个回答
1
投票

方法一:

  1. 创建文件并将其保存在未被监视的目录中。
  2. 将文件移动到正在监视的目录中。

方法二:

创建文件时,使用OnCreated()事件处理程序将文件名添加到filesToIgnore列表中。

OnChanged事件处理程序中,检查filesToIgnore列表是否包含文件名。如果是,请将其从列表中删除(下次处理)并从处理程序返回。

private List<string> filesToIgnore = new List<string>();

private void OnCreated(object source, FileSystemEventArgs file)
{
   filesToIgnore.Add(file.Name);
}

private void OnChanged(object source, FileSystemEventArgs file)
{
    if(filesToIgnore.Contains(file.Name))
    {
         filesToIgnore.Remove(file.Name);
         return; 
    }

    // Code to execute when user saves the file
}

这种方法假设OnCreated()将始终在OnChanged().之前触发


0
投票

这是一个棘手的情况,除了检查文件是否被锁定(假设它有读或写锁)之外,你无能为力

public  bool IsFileLocked(string fileName)
{

   try
   {
      using (var stream = File.Open(fileName, FileMode.Open, FileAccess.Read, FileShare.None))
      {
         return false;
      }   
   }
   catch (IOException)
   {
      //the file is unavailable
      return true;
   }
}

前提是当你得到更新时,你检查文件是否被锁定,如果不是,那么你可以松散地假设它被关闭了。

有些事情要考虑

  1. 检查文件被锁定时,可能存在竞争条件,全部清除,然后找出其他进程已锁定文件。
  2. 我使用过FileAccess.Read,所以这对于只读文件不会失败。
  3. 可以出于各种原因从FileSystemWatcher中删除事件,并且在某些情况下始终不能依赖事件,阅读文档以了解可以删除事件的原因以及如何修复它们。在这些情况下,您可以通过长时间的轮询和一些逻辑来躲避任何落后者(取决于您的情况)

0
投票

我在OnChanged处理程序中添加了以下代码,它似乎按预期工作。

private void OnChanged(object source, FileSystemEventArgs file)
{
    if (file.ChangeType == WatcherChangeTypes.Created)
        return;

    FileInfo fileInfo = new FileInfo(file.FullPath);
    if (fileInfo.CreationTime == fileInfo.LastWriteTime)
        return; 

    // Handle the Changed event
    ...
}

但是,我不确定我是否遗漏了会导致此情况发生的事情。有什么想法吗?


0
投票

下面的扩展方法仅允许处理来自用户操作的事件:

public static void OnChangedByUser(this FileSystemWatcher fsw,
    FileSystemEventHandler handler)
{
    const int TOLERANCE_MSEC = 100;
    object locker = new object();
    string fileName = null;
    Stopwatch stopwatch = new Stopwatch();
    fsw.Created += OnFileCreated;
    fsw.Changed += OnFileChanged;
    fsw.Disposed += OnDisposed;
    void OnFileCreated(object sender, FileSystemEventArgs e)
    {
        lock (locker)
        {
            fileName = e.Name;
            stopwatch.Restart();
        }
    }
    void OnFileChanged(object sender, FileSystemEventArgs e)
    {
        lock (locker)
        {
            if (e.Name == fileName && stopwatch.ElapsedMilliseconds < TOLERANCE_MSEC)
            {
                return; // Ignore this event
            }
        }
        handler.Invoke(sender, e);
    }
    void OnDisposed(object sender, EventArgs e)
    {
        fsw.Created -= OnFileCreated;
        fsw.Changed -= OnFileChanged;
        fsw.Disposed -= OnDisposed;
    }
}

用法示例:

var fsw = new FileSystemWatcher(DIR_NAME);
fsw.OnChangedByUser(File_ChangedByUser);
fsw.EnableRaisingEvents = true;

private static void File_ChangedByUser(object sender, FileSystemEventArgs e)
{
    // Handle the event
}
© www.soinside.com 2019 - 2024. All rights reserved.