File.WriteAllText没有将数据刷新到磁盘

时间:2014-08-18 15:09:30

标签: c# windows io disk

我现在有3个报告,当用户的机器在使用我的软件时崩溃..崩溃与我的程序无关,但是当他们重新启动配置文件时,我的程序写入都已损坏。

如何编写文件没有什么特别之处,只需创建一个Json表示并使用File.WriteAllText()将其转储到磁盘

// save our contents to the disk
string json = JsonConvert.SerializeObject(objectInfo, Formatting.Indented);

// write the contents
File.WriteAllText(path, json);

我有一个用户发给我一个文件,长度看起来是正确的(约3kb),但内容都是0x00。

根据下面的帖子,File.WriteAllText应该关闭文件句柄,将任何未写入的内容刷新到磁盘:

In my C# code does the computer wait until output is complete before moving on?

但正如Alberto在评论中指出的那样:

  

System.IO.File.WriteAllText完成后,将所有文本刷新到   然后,它会被懒惰地写入驱动器。

所以我假设这里发生的事情是文件被清除并用0x00初始化,但是当系统崩溃时数据还没有被写入。

我在考虑使用某种临时文件,所以过程就像这样:

  1. 将新内容写入临时文件
  2. 删除原始文件
  3. 将临时文件重命名为原始文件
  4. 我认为这不会解决问题,因为我认为即使IO仍在等待,Windows也会移动文件。

    有什么方法可以强制机器将数据转储到磁盘而不是决定何时进行,或者更新文件更好?

    更新

    基于@usr,@ mikez和@llya luzyanin的建议,我创建了一个新的WriteAllText函数,使用以下逻辑执行写入:

    1. 使用FileOptions.WriteThrough标志
    2. 创建包含新内容的临时文件
    3. 将数据写入磁盘(在写入完成之前不会返回)
    4. File.Replace将新临时文件的内容复制到真实文件,进行备份
    5. 使用该逻辑,如果最终文件无法加载,我的代码会检查备份文件并加载

      以下是代码:

      public static void WriteAllTextWithBackup(string path, string contents)
      {
          // generate a temp filename
          var tempPath = Path.GetTempFileName();
      
          // create the backup name
          var backup = path + ".backup";
      
          // delete any existing backups
          if (File.Exists(backup))
              File.Delete(backup);
      
          // get the bytes
          var data = Encoding.UTF8.GetBytes(contents);
      
          // write the data to a temp file
          using (var tempFile = File.Create(tempPath, 4096, FileOptions.WriteThrough))
              tempFile.Write(data, 0, data.Length);
      
          // replace the contents
          File.Replace(tempPath, path, backup);
      }
      

1 个答案:

答案 0 :(得分:9)

您可以使用FileStream.Flush强制数据到磁盘。写入临时文件并使用File.Replace以原子方式替换目标文件。

我相信这是有效的。文件系统提供弱保证。这些保证几乎没有记录,而且很复杂。

或者,您可以使用Transactional NTFS(如果可用)。它适用于.NET。

FileOptions.WriteThrough可以替换Flush,但如果您的数据大小超过单个群集,则仍需要临时文件。