如何使用C#安全地将数据保存到现有文件?

如何安全地将数据保存到C#中已存在的文件中? 我有一些序列化到文件的数据,我很确定直接保存到文件不是一个好主意,因为如果出现任何问题,文件将被破坏,以前的版本将丢失。

所以这就是我到目前为止所做的事情:

string tempFile = Path.GetTempFileName(); using (Stream tempFileStream = File.Open(tempFile, FileMode.Truncate)) { SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project)); xmlFormatter.Serialize(tempFileStream, Project); } if (File.Exists(fileName)) File.Delete(fileName); File.Move(tempFile, fileName); if (File.Exists(tempFile)) File.Delete(tempFile); 

问题是,当我尝试保存到Dropbox中的文件时,有时我得到一个exception,告诉我它无法保存到已经存在的文件中。 显然是第一个File.Delete(fileName); 没有立即删除文件,但稍后。 所以我在File.Move(tempFile, fileName);有一个exceptionFile.Move(tempFile, fileName); 因为文件存在然后文件被删除,我的文件丢失了。

我已经在我的Dropbox中使用了其他应用程序和文件,不知何故他们设法不搞乱它。 当我试图保存到我的Dropbox文件夹中的文件时,有时我会收到一条消息,告诉我该文件正在被使用或类似的东西,但我从来没有遇到过被删除文件的问题。

那么这里的标准/最佳做法是什么?

好的,这是我在阅读完所有答案后想出来的:

 private string GetTempFileName(string dir) { string name = null; int attempts = 0; do { name = "temp_" + Player.Math.RandomDigits(10) + ".hsp"; attempts++; if (attempts > 10) throw new Exception("Could not create temporary file."); } while (File.Exists(Path.Combine(dir, name))); return name; } private void SaveProject(string fileName) { bool originalRenamed = false; string tempNewFile = null; string oldFileTempName = null; try { tempNewFile = GetTempFileName(Path.GetDirectoryName(fileName)); using (Stream tempNewFileStream = File.Open(tempNewFile, FileMode.CreateNew)) { SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project)); xmlFormatter.Serialize(tempNewFileStream, Project); } if (File.Exists(fileName)) { oldFileTempName = GetTempFileName(Path.GetDirectoryName(fileName)); File.Move(fileName, oldFileTempName); originalRenamed = true; } File.Move(tempNewFile, fileName); originalRenamed = false; CurrentProjectPath = fileName; } catch (Exception ex) { MessageBox.Show(ex.Message); } finally { if(tempNewFile != null) File.Delete(tempNewFile); if (originalRenamed) MessageBox.Show("'" + fileName + "'" + " have been corrupted or deleted in this operation.\n" + "A backup copy have been created at '" + oldFileTempName + "'"); else if (oldFileTempName != null) File.Delete(oldFileTempName); } } 

Player.Math.RandomDigits只是我创建的一个小函数,用于创建一个包含n个随机数字的字符串。

除非操作系统正在进行,否则我不会看到这会如何搞乱原始文件。 它与Hans的答案非常接近,除了我首先将文件保存到临时文件中,这样,如果在序列化时出现问题,我不需要将文件重命名为它的原始名称,这也可能出错。 请! 如果你发现任何缺陷,请告诉我。

我不确定这有多安全,但假设您的操作系统没有崩溃,请猜猜是什么? 有一个应用程序: File.Replace

 File.Replace(tempFile, fileName, backupFileName); 

我认为在危急情况下你真正需要的是交易 ; 只有这样才能保证不会丢失数据。 看看这篇文章的.NET解决方案,但请注意,它可能比简单的文件替换解决方案更难使用。

这是我通常这样做的方式:

  1. 总是写一个附加自动增量序列号的新文件(比如hello.dat ),或附加时间(只要确保它是唯一的,所以使用滴答或微秒等) – 比如说hello.dat.012345 。 如果该文件存在,请生成另一个号码,然后重试。
  2. 保存后,忘记它。 做其他事情。
  3. 有一个后台进程,不断运行这些新文件。 如果存在,请执行以下操作:
  4. 将原始文件重命名为带有序列号或时间戳的备份hello.dat – > hello.dat.bak.023456
  5. 将最后一个文件重命名为原始名称hello.dat
  6. 删除备份文件
  7. 删除最后一个文件和原始文件之间的所有中间文件(无论如何它们都被最后一个文件覆盖)。
  8. 循环回到#3

如果在#3和#8之间的任何位置发生任何故障,请发送警告消息。 您永远不会丢失任何数据,但临时文件可能会累积。 偶尔清理一下您的目录。