繁体   English   中英

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

[英]FileSystemWatcher: Ignore first change to the file

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

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;

方法一:

  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().

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

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中删除,并且在某些情况下不能总是依赖它; 阅读文档以了解可以删除事件的原因以及如何修复它们。 在这些情况下,您可以通过长时间的轮询和一些逻辑来挑选掉队的人(取决于您的情况)。

我在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
    ...
}

但是,我不确定我是否遗漏了一些会导致它崩溃的东西。 有什么想法吗?

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

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
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM