C# File.WriteAllText未将数据刷新到磁盘

C# File.WriteAllText未将数据刷新到磁盘,c#,windows,io,disk,C#,Windows,Io,Disk,我已经收到3份报告,用户的机器在使用我的软件时崩溃。。崩溃与我的程序无关,但当它们重新启动配置文件时,我的程序写入的文件都已损坏 文件的编写方式没有什么特别之处,只需创建一个Json表示并使用File.WriteAllText()将其转储到磁盘 我让一个用户给我发送了一个文件,长度看起来差不多(~3kb),但内容都是0x00 根据下面的帖子,File.writealText应关闭文件句柄,将任何未写入的内容刷新到磁盘: 但是,正如阿尔贝托在评论中指出的那样: System.IO.File.Wr

我已经收到3份报告,用户的机器在使用我的软件时崩溃。。崩溃与我的程序无关,但当它们重新启动配置文件时,我的程序写入的文件都已损坏

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

我让一个用户给我发送了一个文件,长度看起来差不多(~3kb),但内容都是0x00

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

但是,正如阿尔贝托在评论中指出的那样:

System.IO.File.WriteAllText完成后,将刷新所有文本到 然后,文件系统缓存将被延迟写入驱动器

因此,我假设这里发生的是,文件正在被清除并用0x00初始化,但当系统崩溃时,数据尚未写入

我在考虑使用某种临时文件,因此过程如下:

  • 将新内容写入临时文件
  • 删除原始文件
  • 将临时文件重命名为原始文件
  • 我认为这并不能解决问题,因为我认为即使IO仍然挂起,Windows也会移动文件

    我有没有办法强迫机器将数据转储到磁盘上,而不是让它决定何时进行转储,或者用更好的方式更新文件?

    更新:

    根据@usr、@mikez和@llya luzianin的建议,我创建了一个新的writealText函数,该函数使用以下逻辑执行写操作:

  • 使用FileOptions.WriteThrough标志创建包含新内容的临时文件
  • 将数据写入磁盘(写入完成后才会返回)
  • Replace将新临时文件的内容复制到实际文件,并进行备份
  • 根据这种逻辑,如果最后一个文件未能加载,我的代码将检查备份文件并加载它

    代码如下:

    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);
    }
    

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

    我相信这一定会奏效。文件系统提供的保证很弱。这些担保几乎没有记录在案,而且很复杂

    或者,如果可用,也可以使用事务性NTFS。它可用于.NET


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

    老实说,在这种情况下,我认为您最好花点时间,确定导致系统崩溃的原因。它们只是人们机器上的随机崩溃,与我的系统无关(我认为其中一个被报告为驱动问题)。无论如何,问题是,作为软件开发人员,我的用户要求我确保我的程序在获得BSOD或其他任何情况下不会失去配置。您是否尝试使用
    File.Create
    方法而不是
    File.writealText
    ?它有一个有用的重载-,它接受FileOptions参数,该参数可以设置为
    WriteThrough
    -“指示系统应该通过任何中间缓存写入并直接转到磁盘。”您可以查看手动创建
    FileStream
    ,并使用
    FileOptions.WriteThrough
    选项。问题显示了如何禁用所有缓冲,这只能通过PInvoke完成。@如果您使用WriteThrough写入1GB,我认为直观地认为数据可以部分写入。当调用返回数据时,数据是稳定的,但在此之前,可以混合使用新旧数据。这只会影响临时文件。那么,数据就是丢失了。旧数据保留下来,有意义。我想在这种情况下,丢失新数据可能比损坏的配置要好。是的,拥有旧数据的副本就可以了。。用户丢失了一些信息,但没有丢失much@miked丢失数据是一个无法解决的问题,因为在您开始写之前,机器可能会出现蓝屏。不过,崩溃一致性是可以解决的。我已经用新的写入函数更新了我的问题。。我认为逻辑是正确的?
    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);
    }