Файл, содержащий нулевые значения вместо объекта JSON после перезагрузки компьютера - PullRequest
0 голосов
/ 07 января 2019

Я работаю над проектом C #, в котором у меня есть несколько файлов конфигурации. Каждый из этих файлов содержит объект JSON. В течение всего жизненного цикла программы эти файлы могут быть прочитаны или записаны в различные моменты.

Эта программа управляет промышленной машиной, которую по разным причинам можно отключить в любой момент. Выключение машины приводит к мгновенному выключению компьютера, на котором запущена моя программа. Компьютер работает под управлением Windows 10 Pro x64 с SSD в формате NTFS.

Когда машина включена и, таким образом, моя программа перезапускается, она выдает исключение при чтении файла конфигурации, сообщающего, что файл не содержит объекта JSON. Когда я открываю файл с помощью Блокнота, файл действительно «пустой». Например, вместо объекта JSON:

{ «ключ»: значение }

У меня есть следующий контент:

NULNULNULNULNULNULNULNUL и т. Д.

Свойства файла показывают одинаковый размер файла, независимо от того, содержит ли он объект JSON или «пуст», то же самое относится и к размеру на свойстве диска.

У меня есть другие файлы конфигурации, которые читаются и пишутся, но в виде простого текста, на которые это не влияет.

Эта проблема не возникает при каждом выключении / включении питания и не влияет на каждый файл конфигурации. В основном он появляется с тем же файлом, но не всегда.

Я проверял, правильно ли закрываются файлы конфигурации, когда я их читаю или пишу:

Чтение файла:

JObject jsondata = JObject.Parse (File.ReadAllText (Path));

Записать файл:

File.WriteAllText (Path, jsondata.ToString ());

Оба метода (ReadAllText и WriteAllText) указывают, что они открывают, читают и закрывают файл.

Эти методы окружены предложениями try catch, и у меня никогда не возникало проблем с неправильной структурой JSON или NULL-объектом. Если я прав, даже объект NULL JSON записал бы в файл хотя бы скобки {}.

Я попытался программно сделать резервную копию моих файлов конфигурации в другой папке. Резервное копирование файлов выполняется без чтения файлов (с использованием метода File.Copy ()):

  • Периодически (каждые 10 минут) обновляйте файлы резервных копий самыми последними файлами конфигурации.

  • Если файл конфигурации «пустой» (проверяя, все ли байты в файле равны 0), замените его соответствующим файлом резервной копии.

        // Check if any file has been modified since last check
        for (int file = 0; file < Directory.GetFiles(_FolderToBackup).Length; ++file)
        {
            // Get file to check
            string FilePath = Directory.GetFiles(_FolderToBackup)[file];
            string FileName = Path.GetFileName(FilePath);
    
            // Check if backup file with same name exists in Backup folder
            if (BackupFileExist(FileName))
            {
                // File path to backup file
                string BackupFilePath = _BackupFolder + "\\" + FileName;
    
                // If backup file is empty
                if (isFileEmpty(BackupFilePath))
                {
                    Log.Write("File " + FilePath + " is empty");
    
                    // Copy file to backupfolder, we don't have to check if file to backup is empty, because destination is already empty !
                    File.Copy(FilePath, BackupFilePath, true);
                }
    
                // If file to backup is empty
                if (isFileEmpty(FilePath))
                {
                    Log.Write("File " + FilePath + " is empty");
    
                    // Copy backup file back to folder to backup
                    File.Copy(BackupFilePath, FilePath, true);
                }
    
                // If no file is empty, update only files that have been modified since last check
                if(new FileInfo(FilePath).LastWriteTime > new FileInfo(BackupFilePath).LastWriteTime)
                {
                    File.Copy(FilePath, BackupFilePath, true);
                }
            }
    
            // If backup file does not exist
            else
            {
                string BackupFilePath = Path.Combine(_BackupFolder, FileName);
                File.Copy(FilePath, BackupFilePath);
            }
        }
    

Этот поворот работает отлично, когда файл конфигурации "пуст". Однако иногда, когда я выключал / включал компьютер, и файл конфигурации, и файл резервной копии были пустыми.

Мне также удалось один раз получить пустой файл конфигурации при перезапуске машины, даже если отключение питания происходило, когда мой код не работал.

На данный момент, я не знаю, связана ли моя проблема с выключением / включением питания или с тем, как я читаю / записываю свои файлы:

  • Почему это происходит, когда компьютер выключается / включается?

  • Почему это влияет только на мои файлы конфигурации JSON?

  • Почему файлы очищаются и не портятся?

  • Почему это происходит, даже если файл не открыт в моей программе?

Большое спасибо за ваше время.

Ответы [ 2 ]

0 голосов
/ 07 января 2019

Глядя на источник для File.WriteAllText(), кажется, что ваши данные могут стать жертвой буферизации (кажется, размер буфера в 1 КБ). Если вы хотите гарантировать немедленную запись на диск, вам понадобится собственный метод:

    using (Stream stream = File.Create(yourPath, 64 * 1024, FileOptions.WriteThrough))
    using (TextWriter textWriter = new StreamWriter(stream))
    {
        textWriter.Write(jsonData);
    }
0 голосов
/ 07 января 2019

Не заслуживающий доверия ответ, но прибегающий к помощи "неатомарных окон записи" Я наткнулся на действительно интересную статью, в которой говорится, что то, что вы испытываете, вполне нормально даже для NTFS: https://blogs.msdn.microsoft.com/adioltean/2005/12/28/how-to-do-atomic-writes-in-a-file/

Если я правильно понял, то для вашего варианта использования то, что он рекомендует вам сделать, это:

  • Делайте ваши записи (запись в файл конфигурации JSON) во временный файл
    • (если здесь происходит сбой питания, вы только что потеряли этот раунд изменений, с оригинальным файлом все в порядке)
  • «Очистить записи» (не уверен, как правильно это сделать в вашей среде, но этот вопрос исследует именно это: Как убедиться, что все данные физически записаны на диск? ) или выполните запись с помощью FileOptions.WriteThrough, как описано в @ JesseC.Slicer
    • (если здесь происходит сбой питания, вы только что потеряли этот раунд изменений, с оригинальным файлом все в порядке)
  • Переименуйте исходный файл в формат имен «Я знаю, что я делаю что-то опасное», например, с определенным суффиксом.
    • (при сбое питания у вас нет основного файла конфигурации, вы потеряли этот раунд изменений, но вы все равно можете найти резервную копию)
  • Переименуйте временный файл в окончательное / оригинальное имя
    • (при сбое питания у вас есть основной обновленный файл конфигурации и избыточный устаревший "временно переименованный" файл)
  • Удалить временно переименованный файл

Все это, конечно, предполагает, что вы можете убедиться, что временный файл полностью записан, прежде чем вы начнете переименовывать объекты. Если вам это удалось, то при запуске ваш процесс будет выглядеть примерно так:

  • Если найден «временно переименованный» файл, то либо удалите его (если есть «основной файл»), либо переименуйте его в имя основного файла
  • Загрузить основной файл (никогда не должен быть поврежден)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...