FileSystemWatcher – 文件可以使用

将文件复制到文件观察程序文件夹时,如何确定文件是否已完全复制并可以使用? 因为我在文件复制期间收到多个事件。 (使用File.Copy通过其他程序复制该文件。)

当我遇到这个问题时,我提出的最佳解决方案是不断尝试获取文件的独占锁定; 在写入文件时,锁定尝试将失败,基本上是此答案中的方法。 一旦文件没有被写入,锁定将成功。

不幸的是,唯一的方法是在打开文件时包装一个try / catch,这让我感到畏缩 – 不得不使用try / catch总是很痛苦。 但是,似乎没有任何方法,所以这就是我最终使用的东西。

修改那个答案中的代码就可以了,所以我最终使用了这样的东西:

private void WaitForFile(FileInfo file) { FileStream stream = null; bool FileReady = false; while(!FileReady) { try { using(stream = file.Open(FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { FileReady = true; } } catch (IOException) { //File isn't ready yet, so we need to keep on waiting until it is. } //We'll want to wait a bit between polls, if the file isn't ready. if(!FileReady) Thread.Sleep(1000); } } 

写文件时遇到了这个问题。 在文件完全写入和关闭之前我收到了事件。

解决方案是使用临时文件名并在完成后重命名该文件。 然后注意文件重命名事件而不是文件创建或更改事件。

注意:在一般情况下,此问题无法解决。 如果没有关于文件使用的先验知识,您无法知道其他程序是否已完成对文件的操作。

在您的特定情况下,您应该能够找出File.Copy包含的操作。

最可能的目标文件在整个操作期间被锁定。 在这种情况下,您应该能够简单地尝试打开文件并处理“共享模式违规”exception。

您也可以等待一段时间… – 非常不可靠的选项,但如果您知道文件的大小范围,您可以有合理的延迟让Copy完成。

您还可以“发明”某种事务系统 – 即创建另一个文件,如“destination_file_name.COPYLOCK”,复制文件将在复制“destination_file_name”之前创建该程序,然后删除。

这是一种方法,它将重试文件访问多达X次,并在尝试之间进行Sleep 。 如果它永远无法访问,则应用程序继续:

 private static bool GetIdleFile(string path) { var fileIdle = false; const int MaximumAttemptsAllowed = 30; var attemptsMade = 0; while (!fileIdle && attemptsMade <= MaximumAttemptsAllowed) { try { using (File.Open(path, FileMode.Open, FileAccess.ReadWrite)) { fileIdle = true; } } catch { attemptsMade++; Thread.Sleep(100); } } return fileIdle; } 

它可以像这样使用:

 private void WatcherOnCreated(object sender, FileSystemEventArgs e) { if (GetIdleFile(e.FullPath)) { // Do something like... foreach (var line in File.ReadAllLines(e.FullPath)) { // Do more... } } } 
  private Stream ReadWhenAvailable(FileInfo finfo, TimeSpan? ts = null) => Task.Run(() => { ts = ts == null ? new TimeSpan(long.MaxValue) : ts; var start = DateTime.Now; while (DateTime.Now - start < ts) { Thread.Sleep(200); try { return new FileStream(finfo.FullName, FileMode.Open); } catch { } } return null; }) .Result; 

...当然,您可以修改此方面以满足您的需求。