【问题标题】:C# FileSystemWatcher, How to know file copied completely into the watch folderC# FileSystemWatcher,如何知道文件完全复制到监视文件夹中
【发布时间】:2010-11-25 14:16:15
【问题描述】:

我正在开发一个 .net 应用程序,我在其中使用 FileSystemWatcher 类并将其 Created 事件附加到一个文件夹中。我必须对此事件采取行动(即将文件复制到其他位置)。当我将大尺寸放入附加的监视文件夹时,即使文件复制过程仍未完成,事件也会立即引发。我不想通过 file.open 方法检查这个。

有什么方法可以通知我将文件复制到监视文件夹的过程已经完成,然后我的事件会被触发。

【问题讨论】:

  • 我遇到了同样的问题,此外 FileSystemWatcher 在 Windows 7 中似乎无法正常工作,有时我没有触发事件

标签: c# .net


【解决方案1】:

FileSystemWatcher(以及底层的 ReadDirectoryChangesW API)无法在新文件完全创建时提供通知,这确实令人遗憾。

到目前为止,我遇到的最好和最安全的方法(并且不依赖于计时器)是这样的:

收到 Created 事件后,启动一个线程,在循环中检查文件是否仍处于锁定状态(使用适当的重试间隔和最大重试次数)。检查文件是否被锁定的唯一方法是尝试以独占访问权限打开它:如果成功(不抛出 IOException),则文件完成复制,您的线程可以引发适当的事件(例如 FileCopyCompleted)。

【讨论】:

    【解决方案2】:

    我也遇到了同样的问题,就是这样解决的:

    1. FileSystemWatcher 设置为在文件创建时通知文件被修改时通知。
    2. 收到通知时:

      一个。如果没有为此文件名设置计时器(见下文),请设置一个计时器以在合适的时间间隔内到期(我通常使用 1 秒)。

      b.如果为此文件名设置了计时器,请取消计时器并设置一个新的计时器以在相同的时间间隔内到期。

    当计时器到期时,您知道相关文件已被创建或修改,并且在时间间隔内未被触及。这意味着复制/修改可能已经完成,您现在可以处理它了。

    【讨论】:

      【解决方案3】:

      您可以侦听修改后的事件,并启动计时器。如果修改的事件再次引发,请重置计时器。当计时器达到某个值而没有引发修改事件时,您可以尝试执行复制。

      【讨论】:

        【解决方案4】:

        我订阅了 Changed- 和 Renamed-event 并尝试在每个捕获 IOExceptions 的 Changed-event 上重命名文件。如果重命名成功,则复制完成,Rename-事件仅触发一次。

        【讨论】:

          【解决方案5】:

          FileSystemWatcher 的三个问题,第一个是它可以发送重复的创建事件,因此您可以通过以下方式进行检查:

          this.watcher.Created += (s, e) =>
          {
              if (!this.seen.ContainsKey(e.FullPath) 
                  || (DateTime.Now - this.seen[e.FullPath]) > this.seenInterval)
              {
                  this.seen[e.FullPath] = DateTime.Now;
                  ThreadPool.QueueUserWorkItem(
                      this.WaitForCreatingProcessToCloseFileThenDoStuff, e.FullPath);
              }
          };
          

          其中this.seenDictionary<string, DateTime>this.seenIntervalTimeSpan

          接下来,您必须等待文件创建者完成写入(问题中提出的问题)。第三,你必须小心,因为有时文件创建事件会在文件打开之前被抛出,而不给你FileNotFoundException,但它也可以在你拿到它之前被删除,这也给了FileNotFoundException .

          private void WaitForCreatingProcessToCloseFileThenDoStuff(object threadContext)
          {
              // Make sure the just-found file is done being
              // written by repeatedly attempting to open it
              // for exclusive access.
              var path = (string)threadContext;
              DateTime started = DateTime.Now;
              DateTime lastLengthChange = DateTime.Now;
              long lastLength = 0;
              var noGrowthLimit = new TimeSpan(0, 5, 0);
              var notFoundLimit = new TimeSpan(0, 0, 1);
          
              for (int tries = 0;; ++tries)
              {
                  try
                  {
                      using (var fileStream = new FileStream(
                         path, FileMode.Open, FileAccess.ReadWrite, FileShare.None))
                      {
                          // Do Stuff
                      }
          
                      break;
                  }
                  catch (FileNotFoundException)
                  {
                      // Sometimes the file appears before it is there.
                      if (DateTime.Now - started > notFoundLimit)
                      {
                          // Should be there by now
                          break;
                      }
                  }
                  catch (IOException ex)
                  {
                      // mask in severity, customer, and code
                      var hr = (int)(ex.HResult & 0xA000FFFF);
                      if (hr != 0x80000020 && hr != 0x80000021)
                      {
                          // not a share violation or a lock violation
                          throw;
                      }
                  }
          
                  try
                  {
                      var fi = new FileInfo(path);
                      if (fi.Length > lastLength)
                      {
                          lastLength = fi.Length;
                          lastLengthChange = DateTime.Now;
                      }
                  }
                  catch (Exception ex)
                  {
                  }
          
                  // still locked
                  if (DateTime.Now - lastLengthChange > noGrowthLimit)
                  {
                      // 5 minutes, still locked, no growth.
                      break;
                  }
          
                  Thread.Sleep(111);
              }
          

          当然,您可以设置自己的超时时间。此代码留有足够的时间挂起 5 分钟。如果需要,真实代码也会有一个标志来退出线程。

          【讨论】:

            【解决方案6】:

            这个答案有点晚了,但如果可能的话,我会让源进程在一个或多个大文件之后复制一个小标记文件,并在上面使用 FileWatcher。

            【讨论】:

              【解决方案7】:

              尝试设置过滤器

              myWatcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite;
              

              【讨论】:

              • 这行不通。该事件将被引发多次(在复制文件时每次写入访问一次),并且您无法判断该写入操作是否是完成复制的最后一次。
              • 我检查文件是否有写权限,并决定复制是否完成
              猜你喜欢
              • 2015-12-17
              • 2014-08-31
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2023-03-16
              • 2013-01-18
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多