2015-10-26 3 views
0

У меня есть требование архивировать все данные, используемые для ежедневного создания отчета. Я сжимаю большую часть данных с помощью gzip, так как некоторые из наборов данных могут быть очень большими (10mb +). Я пишу каждый отдельный график protobuf в файл. Я также переименовываю фиксированный набор известных типов малых объектов и добавляю некоторый код, чтобы определить, был ли файл gzipped или нет, когда я его прочитал. Это связано с тем, что маленький файл при сжатии может быть больше, чем несжатый.Gzip только после достижения порога?

К сожалению, только из-за характера данных у меня может быть только несколько элементов более крупного типа объекта, и подход с белым списком может быть проблематичным.

Есть ли способ написать объект потоку и только если он достиг порога (например, 8kb), а затем сжимает его? Я заранее не знаю размер объекта, и иногда у меня есть граф объектов с размером IEnumerable<T>, который может быть значительным по размеру.

Редактировать: Код довольно простой. Я просмотрел тот факт, что храню его в таблице filestream db. Это не имеет особого значения для цели реализации. Я удалил часть постороннего кода.

public Task SerializeModel<T>(TransactionalDbContext dbConn, T Item, DateTime archiveDate, string name) 
{ 
    var continuation = (await dbConn 
     .QueryAsync<PathAndContext>(_getPathAndContext, new {archiveDate, model=name}) 
     .ConfigureAwait(false)) 
     .First(); 

    var useGzip = !_whitelist.Contains(typeof(T)); 

    using (var fs = new SqlFileStream(continuation.Path, continuation.Context, FileAccess.Write, 
     FileOptions.SequentialScan | FileOptions.Asynchronous, 64*1024)) 
    using (var buffer = useGzip ? new GZipStream(fs, CompressionLevel.Optimal) : default(Stream)) 
    { 
     _serializerModel.Serialize(stream ?? fs, item); 
    } 

    dbConn.Commit(); 
} 
+0

Try читать 8k, если есть меньше данных, выход в несжатом. В противном случае gzip весь поток, включая начальный 8k? Какова ваша конкретная проблема? –

+0

Проблема в том, что у меня есть только 'T model', и я передаю ее protobuf' .SerializeWithLengthPrefix', если объект 1 байт или 100 Мб, я понятия не имею. –

+0

Ну, вы подключаете выходной поток. Этот экземпляр потока должен буферизировать 8k данных, а затем решить, что делать. Затем он может передавать распакованные или сжатые данные в другой поток. –

ответ

0

наборы данных могут быть очень большими (10mb +)

На большинстве устройств, которые не очень велика. Есть ли причина, по которой вы не можете прочитать весь объект, прежде чем принимать решение о сжимании? Также обратите внимание на предложение @Niklas прочитать данные о ценности одного буфера (например, 8K), прежде чем принимать решение о сжимании.

Это потому, что небольшой файл при сжатии на самом деле может быть больше, чем несжатый.

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

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

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

если объект 1 байт или 100mb я понятия не имею,

Обратите внимание, что протокол буферов not really designed for large data sets

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

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

Если ваш самый большой объект может удобно сериализовать в память, первый сериализовать в MemoryStream, а затем либо написать, что MemoryStream к конечному пункту назначения, или запустить его через GZipStream, а затем к конечному пункту назначения. Если самый большой объект не может удобно сериализоваться в памяти, я не уверен, какие дальнейшие советы дать.

+0

К сожалению, у меня не всегда есть простой способ прочитать 8 килобайт данных или что-то еще. Я просто модель «Т» как точка входа. В общем, я хочу, чтобы не читать весь объект в основном потому, что я не уверен, как это сделать. 10mb было случайным числом, некоторые на самом деле около 1gb, но что-то выше 10mb может привести к замедлению процесса от gc-связанного давления. Я не отправляю файлы в любом месте, я просто использую их как архив. Для этой цели они работают лучше, чем бинарный журнал. –

+0

Пожалуйста, покажите код, который вы используете для сериализации объекта. –

+0

Код, добавленный в op –

1

Во время сериализации вы можете использовать промежуточный поток для выполнения того, что вы просите. Что-то, как это будет сделать работу

class SerializationOutputStream : Stream 
{ 
    Stream outputStream, writeStream; 
    byte[] buffer; 
    int bufferedCount; 
    long position; 
    public SerializationOutputStream(Stream outputStream, int compressTreshold = 8 * 1024) 
    { 
     writeStream = this.outputStream = outputStream; 
     buffer = new byte[compressTreshold]; 
    } 
    public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } 
    public override void SetLength(long value) { throw new NotSupportedException(); } 
    public override int Read(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } 
    public override bool CanRead { get { return false; } } 
    public override bool CanSeek { get { return false; } } 
    public override bool CanWrite { get { return writeStream != null && writeStream.CanWrite; } } 
    public override long Length { get { throw new NotSupportedException(); } } 
    public override long Position { get { return position; } set { throw new NotSupportedException(); } } 
    public override void Write(byte[] buffer, int offset, int count) 
    { 
     if (count <= 0) return; 
     var newPosition = position + count; 
     if (this.buffer == null) 
      writeStream.Write(buffer, offset, count); 
     else 
     { 
      int bufferCount = Math.Min(count, this.buffer.Length - bufferedCount); 
      if (bufferCount > 0) 
      { 
       Array.Copy(buffer, offset, this.buffer, bufferedCount, bufferCount); 
       bufferedCount += bufferCount; 
      } 
      int remainingCount = count - bufferCount; 
      if (remainingCount > 0) 
      { 
       writeStream = new GZipStream(outputStream, CompressionLevel.Optimal); 
       try 
       { 
        writeStream.Write(this.buffer, 0, this.buffer.Length);        
        writeStream.Write(buffer, offset + bufferCount, remainingCount); 
       } 
       finally { this.buffer = null; } 
      } 
     } 
     position = newPosition; 
    } 
    public override void Flush() 
    { 
     if (buffer == null) 
      writeStream.Flush(); 
     else if (bufferedCount > 0) 
     { 
      try { outputStream.Write(buffer, 0, bufferedCount); } 
      finally { buffer = null; } 
     } 
    } 
    protected override void Dispose(bool disposing) 
    { 
     try 
     { 
      if (!disposing || writeStream == null) return; 
      try { Flush(); } 
      finally { writeStream.Close(); } 
     } 
     finally 
     { 
      writeStream = outputStream = null; 
      buffer = null; 
      base.Dispose(disposing); 
     } 
    } 
} 

и использовать его как этот

using (var stream = new SerializationOutputStream(new SqlFileStream(continuation.Path, continuation.Context, FileAccess.Write, 
     FileOptions.SequentialScan | FileOptions.Asynchronous, 64*1024))) 
    _serializerModel.Serialize(stream, item); 
+0

Это может сработать, я надеялся избежать необходимости писать новый класс потока. –

Смежные вопросы