在繁忙的环境中可靠的文件保存(File.Replace)

我正在处理定期需要将数据保存到磁盘的服务器软件。 我需要确保旧文件被覆盖,并且在出现意外的情况下文件不会被损坏(例如只是部分覆盖)。

我采用了以下模式:

string tempFileName = Path.GetTempFileName(); // ...write out the data to temporary file... MoveOrReplaceFile(tempFileName, fileName); 

… MoveOrReplaceFile是:

 public static void MoveOrReplaceFile( string source, string destination ) { if (source == null) throw new ArgumentNullException("source"); if (destination == null) throw new ArgumentNullException("destination"); if (File.Exists(destination)) { // File.Replace does not work across volumes if (Path.GetPathRoot(Path.GetFullPath(source)) == Path.GetPathRoot(Path.GetFullPath(destination))) { File.Replace(source, destination, null, true); } else { File.Copy(source, destination, true); } } else { File.Move(source, destination); } } 

只要服务器具有对文件的独占访问权,就可以很好地工作。 但是,File.Replace似乎对外部访问文件非常敏感。 任何时候我的软件运行在一个带有杀毒软件或者实时备份系统的系统上,随机的File.Replace错误就会popup:

System.IO.IOException:无法删除要replace的文件。

以下是我已经消除的一些可能的原因:

  • 未释放的文件句柄 :using()确保所有文件句柄尽快释放。
  • 线程问题 :lock()会保护对每个文件的所有访问。
  • 不同的磁盘卷 :跨磁盘卷使用时,File.Replace()失败。 我的方法已经检查,并退回到File.Copy()。

以下是我遇到的一些build议,为什么我不想使用它们:

  • 卷影复制服务 :只有有问题的第三方软件(备份和防病毒监视器等)也使用VSS时才可以使用。 使用VSS需要大量的P / Invoke,并且具有特定于平台的问题。
  • locking文件 :在C#中,locking文件需要保持打开FileStream。 它会保留第三方软件,但1)我仍然无法使用File.Replacereplace文件,2)就像我上面提到的,我宁愿先写一个临时文件,以避免意外腐败。

我会感谢任何获得File.Replace每次工作,或者更一般地,可靠地保存/覆盖磁盘上的文件的任何input。

真的想使用第三个参数,即备份文件名。 这允许Windows简单地重命名原始文件,而不必删除它。 如果任何其他进程打开文件而不删除共享,删除将失败,重命名从来不是一个问题。 您可以在Replace()调用之后自行删除它,并忽略错误。 还要在Replace()调用之前删除它,这样重命名不会失败,并且您将清除先前尝试失败的错误。 粗略地说:

 string backup = destination + ".bak"; File.Delete(backup); File.Replace(source, destination, backup, true); try { File.Delete(backup); } catch { // optional: filesToDeleteLater.Add(backup); } 

有几种可能的方法,这里有一些:

  1. 使用“锁定”文件 – 在操作之前创建的临时文件,并指示其他作者(或读者)文件正被修改并因此被锁定。 操作完成后 – 删除锁定文件。 此方法假定文件创建命令是原子的。
  2. 使用NTFS事务API(如果适用)。
  3. 创建一个指向该文件的链接,用随机名称(例如Guid.NewGuid())写入已更改的文件,然后将链接重新映射到新文件。 所有读者将通过链接(该名称是已知的)来访问该文件。

当然,这三种方法都有其缺点和优点

如果软件正在写入NTFS分区,请尝试使用Transactional NTFS 。 您可以将AlphFS用于.NET包装到API。 这可能是编写文件和防止损坏的最可靠的方法。