Быстрое сжатие на лету с помощью GZipStream

Я сжимаю файл журнала, когда в него записываются данные, что-то вроде:

using (var fs = new FileStream("Test.gz", FileMode.Create, FileAccess.Write, FileShare.None))
{
  using (var compress = new GZipStream(fs, CompressionMode.Compress))
  {
    for (int i = 0; i < 1000000; i++)
    {
      // Clearly this isn't what is happening in production, just 
      // a simply example
      byte[] message = RandomBytes();
      compress.Write(message, 0, message.Length);

      // Flush to disk (in production we will do this every x lines, 
      // or x milliseconds, whichever comes first)
      if (i % 20 == 0)
      {
        compress.Flush();
      }
    }
  }
}

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

Есть ли способ обеспечить читаемый архив после каждого сброса?

Примечание: не обязательно использовать GZipStream, если что-то еще даст нам желаемый результат.

3 ответа

Решение

Один из вариантов - позволить Windows обрабатывать сжатие. Просто включите сжатие в папке, где вы храните свои файлы журналов. При копировании сжатых файлов следует учитывать некоторые соображения производительности, и я не знаю, насколько хорошо сжатие NT выполняется по сравнению с GZipStream или другие варианты сжатия. Возможно, вы захотите сравнить коэффициенты сжатия и загрузку процессора.

Также есть возможность открыть сжатый файл, если вы не хотите включать сжатие для всей папки. Я не пробовал это, но вы можете посмотреть это: http://social.msdn.microsoft.com/forums/en-US/netfxbcl/thread/1b63b4a4-b197-4286-8f3f-af2498e3afe5

Хорошие новости: GZip - это потоковый формат. Поэтому повреждение в конце потока не может повлиять на начало, которое уже было записано.

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

Если вы хотите безошибочное решение, я бы рекомендовал разбивать журнал на один файл каждые x секунд (возможно, x = 1 или 10?). Запишите в файл с расширениями ".gz.tmp" и переименуйте в ".gz" после того, как файл был полностью написан и закрыт.

Да, но это больше, чем просто промывка. Посмотрите на gzlog.h и gzlog.c в дистрибутиве zlib. Он делает именно то, что вам нужно, эффективно добавляя короткие записи журнала в файл gzip и всегда оставляя действительный файл gzip позади. Он также имеет защиту от сбоев или остановок во время процесса, все еще оставляя действительный файл gzip и не теряя записей журнала.

Я рекомендую не использовать GZIPStream. Он глючит и не обеспечивает необходимой функциональности. Вместо этого используйте DotNetZip в качестве интерфейса для zlib.

Другие вопросы по тегам