我已经收到三份关于用户使用我的软件时机器崩溃的报告了... 这些崩溃与我的程序无关,但是当他们重新启动时,我的程序写入的配置文件都会损坏。
文件的写入方式并没有什么特别之处,只是简单地创建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应该关闭文件句柄,并将任何未写入的内容刷新到磁盘:
但是,正如Alberto在评论中指出的那样:
System.IO.File.WriteAllText完成后,将把所有文本刷新到文件系统缓存,然后懒惰地写入驱动器。
因此,我认为这里发生的情况是文件被清除并用0x00初始化,但当系统崩溃时数据尚未写入。
我想可能会使用某种临时文件处理方式,流程如下:
- 将新内容写入临时文件中
- 删除原始文件
- 将临时文件重命名为原始文件
我不认为这会解决问题,因为我认为即使IO仍处于挂起状态,Windows也会移动该文件。
是否有任何方法可以强制机器将数据转储到磁盘而不是决定何时这样做,或者是否有更好的更新文件的方法?
更新:
根据@usr、@mikez和@llya luzyanin的建议,我创建了一个新的WriteAllText函数,使用以下逻辑执行写操作:
- 使用FileOptions.WriteThrough标记创建一个带有新内容的临时文件
- 将数据写入磁盘(直到写入完成才会返回)
- 使用File.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);
}
File.Create
方法代替File.WriteAllText
吗?它有一个有用的重载 - http://msdn.microsoft.com/en-us/library/ms143360(v=vs.110).aspx,它接受FileOptions参数,可以设置为`WriteThrough` - "表示系统应该直接写入磁盘,而不经过任何中间缓存。" - Ilya LuzyaninFileStream
并使用FileOptions.WriteThrough
选项。这个问题展示了如何禁用所有缓存,这只能通过 PInvoke 实现。 - Mike Zboray