Как безопасно сохранить данные в существующий файл с C #? - PullRequest
13 голосов
/ 22 марта 2011

Как безопасно сохранить данные в файл, который уже существует в 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 случайными цифрами.

Я не понимаю, как это могло испортить исходный файл, если ОС не работает. Это довольно близко к ответу Ханса, за исключением того, что я сначала сохраняю файл во временный файл, так что, если что-то пойдет не так при сериализации, мне не нужно переименовывать файл обратно в его первоначальное имя, что также может пойти не так. Пожалуйста! дайте мне знать, если найдете какой-либо недостаток.

Ответы [ 2 ]

7 голосов
/ 22 марта 2011

Я не уверен, насколько это безопасно, но при условии, что ваша ОС не падает, угадайте что?Для этого есть приложение: File.Replace

File.Replace(tempFile, fileName, backupFileName);

Я думаю, что вам действительно нужно в критической ситуации это транзакций ;Только тогда вы можете гарантировать защиту от потери данных.Взгляните на эту статью для решения .NET, но помните, что его может быть немного сложнее, чем простое решение для замены файлов.

3 голосов
/ 22 марта 2011

Я обычно так делаю:

  1. Всегда записывайте в новый файл (скажем, hello.dat) с прикрепленным серийным номером с автоматическим приращением или с указанным временем (просто убедитесь, что он уникален, поэтому используйте тики или микросекунды и т. Д.) - скажем, hello.dat.012345. Если этот файл существует, сгенерируйте другое число и повторите попытку.
  2. После сохранения забудьте об этом. Делай другие вещи.
  3. Иметь фоновый процесс, который продолжает работать через эти новые файлы. Если существует, сделайте следующее:
  4. Переименование исходного файла в резервную копию с серийным номером или отметкой времени hello.dat -> hello.dat.bak.023456
  5. Переименовать последний файл в исходное имя hello.dat
  6. Удалить файл резервной копии
  7. Удалите все промежуточные файлы между последним файлом и исходным файлом (они все равно будут перезаписаны последним файлом).
  8. Вернитесь к # 3

Если между # 3 и # 8 возникнет ошибка, отправьте предупреждающее сообщение. Вы никогда не теряете никаких данных, но временные файлы могут накапливаться. Чистите свой каталог время от времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...