C# - использование protobuf для записи непосредственно в ZIP-файл
Я работаю над программой-симулятором, которая часто записывает небольшие данные (<1 КБ) (> 10 раз в секунду) в файл. Я использовал Очередь для хранения этих данных, и когда в этой очереди было 300 или более членов, она записывает данные в файл, сохраненный в zip-файле, и очищает очередь.
Проблема после приостановки или остановки симуляции (в обоих случаях flush
будет вызван), когда я извлекаю zip-файл и проверяю выходные файлы (более конкретно: Offsets.sdo
) только данные с последнего шага написания (в моем коде, вызов flush
) находится внутри файла. Также во время моделирования Zipfile, который я создаю (с outFile
поле) имеет размер 0.
Мой код писателя файла:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.IO.Packaging;
using System.Net.Mime;
using ProtoBuf;
namespace SEWS.History
{
class HistoryWriterEngine : HistoryEngine ,HistoryWriter
{
public static readonly string OUTPUT_DATA_FILE_NAME = "SimulationOutdata.sod";
public static readonly string OFFSETS_FILE_NAME = "Offsets.sdo";
#region buffer
private static readonly int MAXIMUM_BUFFER_SIZE = 300; // half a minute of simulation with 0.1s steps
private Queue<SEWS.SimulationEngine.SimulationOutputData> buffer = new Queue<SEWS.SimulationEngine.SimulationOutputData>();
private Dictionary<string, PackagePart> packageParts = new Dictionary<string, PackagePart>();
#endregion
#region IO
private ZipPackage outFile;
public override void initFile(string address)
{
if (outFile != null)
{
flush();
closeFile();
}
workingFile = address;
outFile = (ZipPackage)ZipPackage.Open(workingFile, FileMode.Create);
getNewStream(OUTPUT_DATA_FILE_NAME);
getNewStream(OFFSETS_FILE_NAME, MediaTypeNames.Text.Plain);
}
public override void closeFile()
{
flush();
outFile.Flush();
outFile.Close();
outFile = null;
}
public Stream getNewStream(string fileName, string type = MediaTypeNames.Application.Octet)
{
PackagePart packagePart;
packagePart = outFile.CreatePart(
PackUriHelper.CreatePartUri(new Uri(fileName, UriKind.Relative)),
type,
CompressionOption.SuperFast);
packageParts.Add(fileName, packagePart);
return packagePart.GetStream();
}
public Stream getStream(string fileName)
{
if (packageParts.ContainsKey(fileName))
{
return (packageParts[fileName] as PackagePart).GetStream();
}
else
{
throw new NullReferenceException("No such file as " + fileName + " found.");
}
}
#endregion
#region HistoryWriterImpl
public void writeOutputData(SEWS.SimulationEngine.SimulationOutputData data)
{
buffer.Enqueue(data);
if (buffer.Count >= MAXIMUM_BUFFER_SIZE)
{
flush();
}
}
System.Diagnostics.Stopwatch s = new System.Diagnostics.Stopwatch();
public void flush()
{
Stream outStream = getStream(OUTPUT_DATA_FILE_NAME);
Stream offsetsStream = getStream(OFFSETS_FILE_NAME);
StreamWriter offsetsWriter = new StreamWriter(offsetsStream);
SEWS.SimulationEngine.SimulationOutputData currentData;
Console.WriteLine("Writing " + buffer.Count + " records");
s.Restart();
while (buffer.Count > 0)
{
currentData = buffer.Dequeue();
Serializer.SerializeWithLengthPrefix(outStream, currentData, PrefixStyle.Base128, 1);
offsetsWriter.Write(
new StringBuilder()
.Append(currentData.CurrentStep.ToString())
.Append(' ')
.Append(currentData.CurrentTime.TimeSinceStart.ToString())
.Append(' ')
.Append(outStream.Position)
.Append("\r\n")
.ToString()
);
}
s.Stop();
Console.WriteLine("Took " + s.ElapsedMilliseconds + " ms.");
outStream.Flush();
offsetsWriter.Flush();
offsetsStream.Flush();
outStream.Close();
offsetsWriter.Close();
offsetsStream.Close();
outFile.Flush();
}
#endregion
}
}
Выход:
Writing 300 records
Took 138 ms.
Writing 300 records
Took 18 ms.
Writing 300 records
Took 14 ms.
Writing 300 records
Took 14 ms.
Writing 300 records
Took 14 ms.
Writing 41 records
Took 5 ms.
Первые 5 записей являются автоматическими, а последняя (41 запись) относится к паузе в симуляции.
1 ответ
Ваш код неоднократно звонит flush()
и намерение внутри flush()
кажется, чтобы повторно использовать файл через getStream()
,
Но в хвостовой части flush()
есть вызов outStream.Close();
и из этого следует, что getStream()
всегда должен создавать новый файл. В режиме перезаписи видимо.