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

时间:2019-04-23 23:04:48

标签: c# filesystemwatcher

每当用户在修改文件后保存文件时,我正在使用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;

4 个答案:

答案 0 :(得分: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().之前触发

答案 1 :(得分: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删除事件,在某些情况下不能总是依靠它,请阅读文档以了解为什么可以删除事件以及如何修复它们。在这种情况下,您可能需要进行长时间的投票和一些逻辑来选择散乱的人(取决于您的情况)

答案 2 :(得分: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
    ...
}

但是,我不确定是否丢失了会导致这种情况中断的内容。有什么想法吗?

答案 3 :(得分: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
}