File.WriteAllText 不将数据刷新到磁盘 [英] File.WriteAllText not flushing data to disk

查看:32
本文介绍了File.WriteAllText 不将数据刷新到磁盘的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've had 3 reports now of user's machines crashing while using my software.. the crashes are not related to my program but when they restart the config files my program writes are all corrupt.

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

There is nothing special to how the files are being written, simply creating a Json representation and dumping it to disk using File.WriteAllText()

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

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

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

I've had a user send me one of the files and the length looks about right (~3kb) but the contents are all 0x00.

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

According to the post below File.WriteAllText should close the file handle, flushing any unwritten contents to the disk:

在我的 C# 代码中,计算机是否会等到输出完成后再继续?

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

BUT, as pointed out by Alberto in the comments:

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

System.IO.File.WriteAllText when completes, will flush all the text to the filesystem cache, then, it will be lazily written to the drive.

所以我推测这里发生的事情是文件正在被清除并使用 0x00 进行初始化,但是当系统崩溃时数据尚未写入.

So I presume what is happening here is that the file is being cleared and initialized with 0x00 but the data is not yet written when the system crashes.

我正在考虑使用某种临时文件,所以过程会是这样的:

I was thinking of maybe using some sort of temp file so the process would be like this:

  1. 将新内容写入临时文件
  2. 删除原始文件
  3. 将临时文件重命名为原始文件

我认为这不会解决问题,因为我认为即使 IO 仍处于挂起状态,Windows 也会移动文件.

I don't think that will solve the problem as I presume Windows will just move the file even though the IO is still pending.

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

更新:

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

Based on suggestions by @usr, @mikez and @llya luzyanin I've created a new WriteAllText function that performs the write using the following logic:

  1. 使用 FileOptions.WriteThrough 标志创建一个包含新内容的临时文件
  2. 将数据写入磁盘(在写入完成之前不会返回)
  3. File.Replace 将新临时文件的内容复制到真实文件中,进行备份

按照这种逻辑,如果最终文件无法加载,我的代码会检查备份文件并加载它

With that logic, if the final file fails to load, my code an check for a backup file and load that instead

代码如下:

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 原子地替换目标文件.

You can use FileStream.Flush to force the data to disk. Write to a temp file and use File.Replace to atomically replace the target file.

我相信这可以保证有效.文件系统提供较弱的保证.这些保证几乎没有记录在案,而且很复杂.

I believe this is guaranteed to work. File systems give weak guarantees. These guarantees are hardly ever documented and they are complex.

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

Alternatively, you can use Transactional NTFS if available. It is available for .NET.

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

FileOptions.WriteThrough can replace Flush but you still need the temp file if your data can exceed a single cluster in size.

这篇关于File.WriteAllText 不将数据刷新到磁盘的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆