如何使用 C# 安全地将数据保存到现有文件?

发布于 2024-10-25 20:44:38 字数 2794 浏览 4 评论 0原文

如何安全地将数据保存到 C# 中已存在的文件中?我有一些数据被序列化到文件中,我很确定直接安全到文件不是一个好主意,因为如果出现任何问题,文件将被损坏,以前的版本将丢失。

这就是我到目前为止一直在做的事情:

string tempFile = Path.GetTempFileName();

using (Stream tempFileStream = File.Open(tempFile, FileMode.Truncate))
{
    SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project));
    xmlFormatter.Serialize(tempFileStream, Project);
}

if (File.Exists(fileName)) File.Delete(fileName);
File.Move(tempFile, fileName);
if (File.Exists(tempFile)) File.Delete(tempFile);

问题是,当我尝试保存到 Dropbox 中的文件时,有时我收到一个异常,告诉我它无法保存到已经存在的文件中。显然第一个 File.Delete(fileName); 并没有立即删除文件,而是过了一会儿才删除。因此,我在 File.Move(tempFile, fileName); 中遇到异常,因为该文件存在,然后该文件被删除,我的文件丢失了。

我在 Dropbox 中使用过其他应用程序来处理文件,不知怎的,他们设法不把它弄乱。当我尝试保存到 Dropbox 文件夹中的文件时,有时会收到一条消息,告诉我该文件正在使用或类似的信息,但我从未遇到过文件被删除的问题。

那么这里的标准/最佳实践是什么?

好的,这就是我在阅读所有答案后想到的:

private string GetTempFileName(string dir)
{
    string name = null;
    int attempts = 0;
    do
    {
        name = "temp_" + Player.Math.RandomDigits(10) + ".hsp";
        attempts++;
        if (attempts > 10) throw new Exception("Could not create temporary file.");
    }
    while (File.Exists(Path.Combine(dir, name)));

    return name;
}

private void SaveProject(string fileName)
{
    bool originalRenamed = false;
    string tempNewFile = null;
    string oldFileTempName = null;

    try
    {
        tempNewFile = GetTempFileName(Path.GetDirectoryName(fileName));

        using (Stream tempNewFileStream = File.Open(tempNewFile, FileMode.CreateNew))
        {
            SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project));
            xmlFormatter.Serialize(tempNewFileStream, Project);
        }

        if (File.Exists(fileName))
        {
            oldFileTempName = GetTempFileName(Path.GetDirectoryName(fileName));
            File.Move(fileName, oldFileTempName);
            originalRenamed = true;
        }

        File.Move(tempNewFile, fileName);
        originalRenamed = false;

        CurrentProjectPath = fileName;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        if(tempNewFile != null) File.Delete(tempNewFile);

        if (originalRenamed) MessageBox.Show("'" + fileName + "'" +
            " have been corrupted or deleted in this operation.\n" +
            "A backup copy have been created at '" + oldFileTempName + "'");
        else if (oldFileTempName != null) File.Delete(oldFileTempName);
    }
}

Player.Math.RandomDigits 只是我制作的一个小函数,它创建一个包含 n 个随机数字的字符串。

我不明白这怎么会弄乱原始文件,除非操作系统变得疯狂。这与汉斯的答案非常接近,只是我首先将文件保存到临时文件中,这样,如果序列化时出现问题,我不需要将文件重命名回原来的名称,这也可能会出错。请!如果您发现任何缺陷,请告诉我。

How do you safely save data to a file that already exists in C#? I have some data that is serialized to a file and I'm pretty sure is not a good idea to safe directly to the file because if anything goes wrong the file will get corrupted and the previous version will get lost.

So this is what I've been doing so far:

string tempFile = Path.GetTempFileName();

using (Stream tempFileStream = File.Open(tempFile, FileMode.Truncate))
{
    SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project));
    xmlFormatter.Serialize(tempFileStream, Project);
}

if (File.Exists(fileName)) File.Delete(fileName);
File.Move(tempFile, fileName);
if (File.Exists(tempFile)) File.Delete(tempFile);

The problem is that when I tried to save to a file that was in my Dropbox, sometimes I got an exception telling me that it cannot save to a file that already exists. Apparently the first File.Delete(fileName); didn't delete the file right away but after a little bit. So I got an exception in the File.Move(tempFile, fileName); because the file existed and then the file got erased and my file got lost.

I've used other applications with files in my Dropbox and somehow they manage to not mess it up. When I'm trying to save to a file in my Dropbox folder, sometimes I get a message telling me that the file is being used or stuff like that but I never had a problem with a file being erased.

So what would it be the standard / best practice here?

OK this is what I came up with after reading all answers:

private string GetTempFileName(string dir)
{
    string name = null;
    int attempts = 0;
    do
    {
        name = "temp_" + Player.Math.RandomDigits(10) + ".hsp";
        attempts++;
        if (attempts > 10) throw new Exception("Could not create temporary file.");
    }
    while (File.Exists(Path.Combine(dir, name)));

    return name;
}

private void SaveProject(string fileName)
{
    bool originalRenamed = false;
    string tempNewFile = null;
    string oldFileTempName = null;

    try
    {
        tempNewFile = GetTempFileName(Path.GetDirectoryName(fileName));

        using (Stream tempNewFileStream = File.Open(tempNewFile, FileMode.CreateNew))
        {
            SafeXmlSerializer xmlFormatter = new SafeXmlSerializer(typeof(Project));
            xmlFormatter.Serialize(tempNewFileStream, Project);
        }

        if (File.Exists(fileName))
        {
            oldFileTempName = GetTempFileName(Path.GetDirectoryName(fileName));
            File.Move(fileName, oldFileTempName);
            originalRenamed = true;
        }

        File.Move(tempNewFile, fileName);
        originalRenamed = false;

        CurrentProjectPath = fileName;
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
    finally
    {
        if(tempNewFile != null) File.Delete(tempNewFile);

        if (originalRenamed) MessageBox.Show("'" + fileName + "'" +
            " have been corrupted or deleted in this operation.\n" +
            "A backup copy have been created at '" + oldFileTempName + "'");
        else if (oldFileTempName != null) File.Delete(oldFileTempName);
    }
}

Player.Math.RandomDigits is just a little function I made that creates an string with n random digits.

I don't see how could this mess up the original file unless the OS is going wacko. It's pretty close to Hans's answer except that I first save the file to a temporary file so that, if something goes wrong when serializing, I don't need to rename the file back to it's original name, which can also go wrong. Please! let me know if you find any flaw.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

梦在夏天 2024-11-01 20:44:38

我不确定这有多安全,但假设你的操作系统没有崩溃,你猜怎么着?有一个应用程序:File.Replace

File.Replace(tempFile, fileName, backupFileName);

我想你<在紧急情况下真正需要的是事务;只有这样你才能保证数据不丢失。查看本文了解 .NET 解决方案,但请注意,它可能是一个比简单的文件替换解决方案更难使用。

I'm not sure how secure this is, but assuming your OS isn't crashing, guess what? There's an app for that: File.Replace

File.Replace(tempFile, fileName, backupFileName);

I think what you really need in a critical situation is transactions; only then can you guarantee against the loss of data. Take a look at this article for a .NET solution, but be aware that it might be a bit harder to use than a simple file replacement solution.

紫轩蝶泪 2024-11-01 20:44:38

这是我通常这样做的方式:

  1. 始终写入一个新文件(例如 hello.dat),并附加自动增量序列号或附加时间(只需确保它是唯一的,所以使用刻度或微秒等)——说hello.dat.012345。如果该文件存在,请生成另一个号码并重试。
  2. 保存后,忘记它。做其他事情。
  3. 有一个后台进程不断运行这些新文件。如果存在,则执行以下操作:
  4. 将原始文件重命名为带有序列号或时间戳的备份 hello.dat -> hello.dat.bak.023456
  5. 将最后一个文件重命名为原来的名称 hello.dat
  6. 删除备份文件
  7. 删除最后一个文件与原始文件之间的所有中间文件(它们无论如何都被最后一个文件覆盖)。
  8. 循环回到#3

如果#3 和#8 之间有任何故障,则发送警告消息。您永远不会丢失任何数据,但临时文件可能会累积。偶尔清理一下你的目录。

This is the way I usually do it:

  1. Always write to a new file (say hello.dat) with an auto-increment serial number attached, or the time attached (just make sure that it is unique, so use ticks or microseconds etc.) -- say hello.dat.012345. If that file exists, generate another number and try again.
  2. After saving, forget about it. Do other things.
  3. Have a background process that keeps running through these new files. If exists, do the following:
  4. Rename the original file to a backup with a serial number or timestamp hello.dat -> hello.dat.bak.023456
  5. Rename the last file to the original name hello.dat
  6. Remove the backup file
  7. Remove all intermediate files between the last file and the original file (they are all overwritten by the last file anyway).
  8. Loop back to #3

If there is any failure anywhere between #3 and #8, send a warning message. You're never losing any data, but temp files may build up. Clean your directory once in a while.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文