Страницы

Поиск по вопросам

пятница, 20 декабря 2019 г.

Как защитить файл от повреждения при отключении питания?

#c_sharp #xml


Программа хранит ряд настроек в xml-файле. Если в момента записи настройки отключается
питание компьютера, содержимое файла пропадает (сам файл остается, но если открыть
- он пустой). Появилась идея сохранять изменения во временный файл, а затем переносить
их в основной. Тогда, теоретически, должен повредиться только один из файлов при сбросе
питания. Но на деле пропадает содержимое обоих файлов. Причина в коде? Или сам подход
неправильный?
Сохранение временного файла и его копирование в файл-оригинал представлено ниже:

StreamWriter streamWriter = new StreamWriter(FileName_);
BlockFile.WaitOne();
xmlDoc.Save(streamWriter);                    
BlockFile.Release();
streamWriter.Dispose();
File.Copy(FileName_, FileName, true);


В настоящее время код метода выглядит так:

static public void SetValueOfParameter(string nodeName, string parameterName, string
valueOfParameter)
{            
    BlockFile.WaitOne();
    XmlDocument xmlDoc = new XmlDocument();
    using (StreamReader streamReader = new StreamReader(FileName))
    {
        xmlDoc.Load(streamReader);
        XmlElement xRoot = xmlDoc.DocumentElement;
        XmlNode node = xRoot.SelectSingleNode(nodeName);
        node.SelectSingleNode(parameterName).InnerText = valueOfParameter;
        streamReader.Close();
        streamReader.Dispose();
    }
    using (StreamWriter streamWriter = new StreamWriter(FileName_))
    {
        xmlDoc.Save(streamWriter);
        streamWriter.Flush();
        streamWriter.Close();
        streamWriter.Dispose();
    }

    FileInfo fInfo = new FileInfo(FileName_);
    if (fInfo.Exists && fInfo.Length>0)
    {
        if (File.Exists(FileName))
        {
            File.Copy(FileName_,FileName,true);
        }
    }
    BlockFile.Release();
}


Вместо File.Copy(FileName_,FileName,true); было испробовано удаление/перемещение.
Эффект тот же - два файла пустые. Кэширование диска снято
    


Ответы

Ответ 1



Судя по описанию проблемы - пропаданию данных уже после того, как они были записаны на диск (а они были записаны, т.к. вы смогли скопировать файл) - у вас включено кэширование записи на диск. Проверить можно в свойствах диска в Device Manager. Кэширование записи безопасно только на дисках с автономным питанием (рейдах с батарейкой). Именно поэтому оно отключено по умолчанию. Если у вас обычный десктопный диск - данные будут теряться, и из кода на C# вы это никак не поборете.

Ответ 2



Рекомендую сделать так: Вы сохраняете новый файл с именем settings_new.xml В случае успеха (проверка файла settings_new.xml на существование и ненулевую длину) удаляете файл settings.xml Переименовываете файл settings_new.xml в settings.xml (можно так же скопировать и удалить оригинал) При новом запуске приложения сначала ищете файл settings_new.xml. Если он существует и не пуст, значит произошел сбой питания или некорректное завершение работы программы. В этом случае доделываете операции 2 и 3. Если вы выполните этот алгоритм, то при любом сбое вы будете уверены настройки не пропадут.

Ответ 3



Могу еще предложить не такой красивый, но гарантированно сохраняющий данные вариант: Создайте папку settings, сохраняйте файлы туда с timestamp, например 1494856800.xml При запуске программы ищите наиболее новый файл (максимальный timestamp) с ненулевой длинной. Удаляете все файлы кроме найденного (эту операцию для надежности можно поменять на "Удаляете все файлы, кроме 10 последних") UPD: Добавил пример кода: static public void SetValueOfParameter(string nodeName, string parameterName, string valueOfParameter) { // BlockFile.WaitOne(); string old_filename = get_setting_filename(); string new_filename = DateTime.Now.ToFileTimeUtc().ToString(); XmlDocument xmlDoc = new XmlDocument(); using (StreamReader streamReader = new StreamReader(old_filename)) { xmlDoc.Load(streamReader); XmlElement xRoot = xmlDoc.DocumentElement; XmlNode node = xRoot.SelectSingleNode(nodeName); node.SelectSingleNode(parameterName).InnerText = valueOfParameter; streamReader.Close(); streamReader.Dispose(); } using (StreamWriter streamWriter = new StreamWriter(new_filename)) { xmlDoc.Save(streamWriter); streamWriter.Flush(); streamWriter.Close(); streamWriter.Dispose(); } remove_old_files(); // BlockFile.Release(); } static public string get_setting_filename() { DirectoryInfo d = new DirectoryInfo("settings"); FileInfo[] Files = d.GetFiles("*.xml"); DateTime min_date = DateTime.MinValue; string filename = ""; // ищем не пустой файл с максимальной датой foreach (FileInfo file in Files) { // 4 - Чтобы файл не состоял только из символа окончания файла if (file.LastWriteTimeUtc > min_date && file.Length > 4) { min_date = file.LastWriteTimeUtc; filename = file.Name; } } return filename; } static public void remove_old_files() { DirectoryInfo d = new DirectoryInfo("settings"); FileInfo[] Files = d.GetFiles("*.xml"); DateTime min_date = DateTime.MinValue; int count_save_files = 10; foreach (FileInfo file in Files.OrderBy(f => f.LastWriteTime).Reverse().ToList()) { // Проверяем что файл не пустой if (file.Length > 4) { // Не удаляем файлы настроек за последние сутки, независимо от количества if (file.LastWriteTime < DateTime.Now.AddDays(-1)) { // Сохраняем 10 последних файлов (помимо последних суток) if (count_save_files > 0) { count_save_files--; } else { File.Delete(file.Name); } } } else { // Удаляем любые поврежденные файлы File.Delete(file.Name); } } }

Ответ 4



Доброго времени суток! Последовательность должна быть такой: Вариант 1: Открытие и работа с файлом 1) Проверяете существование ~settings.xml 1.1) Проверяете валидность файла ~settings.xml 2.1) Если существует и валиден то открываете его с доступом на чтение иначе удаляете его и переходите к пункту 3 2.2) Открываете файл settings.xml с доступом на запись 2.3) Записываете содержимое файла ~settings.xml в settings.xml 2.4) Закрываете файлы. Удаляете ~settings.xml (далее работает по пунктам следующим пунктам) 3) Если файл ~settings.xml не существует, то открываете файл settings.xml с доступом на чтение 4) Создаете файл ~settings.xml и открываете с доступом на запись 5) Работаете с файлом ~settings.xml Сохранение результатов 1) Закрываете оба файла 2) Открываете файл ~settings.xml с доступом на чтение и файл settings.xml с доступом на запись 3) Перезаписываете файл settings.xml содержимым файла ~settings.xml 4) Закрываете оба файла. Удаляете ~settings.xml Вариант 2: Открытие и работа с файлом 1) Проверяете существование ~settings.xml 1.1) Проверяете валидность файла ~settings.xml 2.2) Если ~settings.xml существует и валиден то удаляете settings.xml иначе удяляете его и переходите к пункту 3 2.3) Копируете файл ~settings.xml и создаете новый файл с названием settings.xml 2.4) Удаляете ~settings.xml (далее работает по пунктам следующим пунктам) 3) Если файл ~settings.xml не существует, то копируете settings.xml и создаете новый файл с названием ~settings.xml 4) Работаете с файлом ~settings.xml (На этом этапе можно открыть файл ~settings.xml с доступом на запись) Сохранение результатов 1) Закрываете файл ~settings.xml 2) Удаляете файл settings.xml 3) Копируете файл ~settings.xml и создаете файл с названием settings.xml 4) Удаляете ~settings.xml Этот алгоритм должен предотвратить полную потерю данных

Ответ 5



xml заменили на базу sqlite. Все наладилось

Комментариев нет:

Отправить комментарий