Как безопасно сохранять данные в существующий файл с помощью С#?
Как вы безопасно сохраняете данные в файл, который уже существует на С#? У меня есть некоторые данные, которые сериализованы в файл, и я уверен, что это не очень хорошая идея для безопасного обращения к файлу, потому что, если что-то пойдет не так, файл будет поврежден, а предыдущая версия будет потеряна.
Так вот что я делаю до сих пор:
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 случайными цифрами.
Я не вижу, как это может испортить исходный файл, если ОС не собирается wacko. Это довольно близко к ответу Ханса, за исключением того, что я сначала сохраняю файл во временном файле, чтобы, если что-то пошло не так, когда сериализуется, мне не нужно переименовывать файл обратно к нему с оригинальным именем, что также может пойти не так. Пожалуйста! сообщите мне, если вы обнаружите недостатки.
Ответы
Ответ 1
Я не уверен, насколько это безопасно, но если ваша ОС не сбой, угадайте, что? Там есть приложение для этого: File.Replace
File.Replace(tempFile, fileName, backupFileName);
Я думаю, что вам действительно нужно в критической ситуации транзакции; только тогда вы можете гарантировать от потери данных. Взгляните на эту статью для решения .NET, но имейте в виду, что это может быть немного сложнее в использовании, чем простое решение для замены файлов.
Ответ 2
Так я обычно это делаю:
- Всегда пишите в новый файл (скажем
hello.dat
) с прилагаемым серийным номером автоматического приращения или приложенным временем (просто убедитесь, что он уникален, поэтому используйте тики или микросекунды и т.д.) - скажите hello.dat.012345
. Если этот файл существует, сгенерируйте еще один номер и повторите попытку.
- После сохранения, забудьте об этом. Делайте другие вещи.
- У вас есть фоновый процесс, который продолжает работать через эти новые файлы. Если существует, выполните следующие действия:
- Переименуйте исходный файл в резервную копию с серийным номером или меткой времени
hello.dat
→ hello.dat.bak.023456
- Переименуйте последний файл в исходное имя
hello.dat
- Удалить файл резервной копии
- Удалите все промежуточные файлы между последним файлом и исходным файлом (все они все равно будут перезаписаны последним файлом).
- Возвратитесь к # 3
Если какой-либо сбой произошел где-то между # 3 и # 8, отправьте предупреждающее сообщение. Вы никогда не теряете никаких данных, но временные файлы могут накапливаться. Периодически очищайте свой каталог.