【问题标题】:Best practice for writing big files编写大文件的最佳实践
【发布时间】:2018-12-07 08:01:05
【问题描述】:

我需要在我的项目中写一个大文件。


我学到了什么:

  • 我不应该将大文件直接写入目标路径, 因为这可能会留下一个不完整的文件,以防应用在编写时崩溃。

  • 相反,我应该写入一个临时文件并移动(重命名)它。 (称为原子文件操作)


我的代码 sn-p:

[NotNull]
public static async Task WriteAllTextAsync([NotNull] string path, [NotNull] string content) 
{
    string temporaryFilePath = null;
    try {
        temporaryFilePath = Path.GetTempFileName();
        using (var stream = new StreamWriter(temporaryFilePath, true)) {
            await stream.WriteAsync(content).ConfigureAwait(false);
        }            

        File.Delete(path);
        File.Move(temporaryFilePath, path);
    }
    finally {
        if (temporaryFilePath != null) File.Delete(temporaryFilePath);
    }
}

我的问题:

  • 如果应用在File.DeleteFile.Move 之间崩溃,该文件将会丢失。我可以避免这种情况吗?

  • 还有其他编写大文件的最佳实践吗?

  • 对我的代码有什么建议吗?

【问题讨论】:

  • 采用这种方法时需要注意的一点:如果 TEMP 文件夹与最终目标文件夹位于不同的驱动器上,那么 File.Move() 实际上必须复制该文件,它是一个非常大的文件,可能会严重影响性能。
  • @Matthew 是的,我知道。在这种情况下,我有足够的理由断定目标文件夹和临时文件夹在同一个驱动器上。所以我只添加了finally 块进行确认(File.Move 当源和目标位于不同的驱动器上时复制)。但如果我能避免性能问题,那就更好了。有什么解决办法吗?我对你的想法很感兴趣。
  • @SirRufo 感谢您提供的信息丰富的评论。我不知道FileOptions.DeleteOnClose 选项。恐怕我不明白如何将其应用于我的案例。你不能Move一个文件在关闭之前(因此在被删除之前)吗?如果你再帮我一些,我会被申请的......
  • 好吧,我会写临时文件,复制到目的地(就像移动到不同的驱动器时一样)并删除临时文件。

标签: c# .net system.io.file


【解决方案1】:

如果应用程序在 File.Delete 和 File.Move 之间崩溃,该文件将会丢失。我可以避免这种情况吗?

我不知道,但您可以检测到它 - 如果您使用更可预测的文件名,您可以从中恢复。如果您稍微调整该过程以使用三个文件名,这会有所帮助:目标、“新”文件和“旧”文件。流程变成:

  • 写入“新”文件(例如foo.txt.new
  • 将目标文件重命名为“旧”文件(例如foo.txt.old
  • 将“新”文件重命名为目标文件
  • 删除“旧”文件

然后您有三个文件,每个文件可能存在或不存在。这可以帮助您在阅读新文件时检测情况:

  • 没有文件:尚未写入任何数据
  • 目标:一切顺利
  • 目标和新:应用程序在写入新文件时崩溃
  • 目标和旧:应用无法删除旧文件
  • 新旧:应用程序在第一次重命名后失败,但在第二次重命名之前
  • 所有三个,或者只是旧的,或者只是新的:发生了一些非常奇怪的事情!用户可能干扰了

注意:我以前不知道File.Replace,但我怀疑它实际上只是一种更简单且可能更有效的方式来处理你已经在做的代码。 (太好了 - 使用它!)但恢复过程仍然是相同的。

【讨论】:

  • 使用事务性 NTFS 可以避免问题。 Microsoft 在 .NET 中从来没有任何 NTFS 功能,因此必须为此使用库 AlphaFS。使用 AlphaFS 可以直接写入目标文件,只有在事务提交时才会出现更改
  • @PanagiotisKanavos:这听起来像是一个很好的答案:)
  • 不幸的是,微软表示该功能已被弃用,并将在未来删除,因为 ReFS 不需要它。那是在他们从 Windows 10 中删除 ReFS 之前。The current article 使用了更温和的措辞,但仍建议将ReplaceFile 作为“一种”替代方案
  • @PanagiotisKanavos:骗子。不过感谢您提供的详细信息-总是很高兴了解更多信息:)
  • 旁注:可能不是 Delete(current);Rename(new,old) 而是 Delete(backup);Rename(current, backup); Rename(new,current);... 因此,您永远不会在 Delete 和 Move 之间永远丢失数据。
【解决方案2】:

您可以使用File.Replace 而不是删除和移动文件。如果发生硬故障(断电或类似情况),您将始终丢失数据,您必须计算在内。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-27
  • 1970-01-01
  • 2010-12-15
  • 2012-10-12
  • 1970-01-01
  • 1970-01-01
  • 2023-03-06
相关资源
最近更新 更多