C # MemoryStream & GZipInputStream: не может. Читать более 256 байт - PullRequest
2 голосов
/ 09 октября 2019

У меня проблема с записью несжатого потока GZIP с использованием GZipInputStream от SharpZipLib. Кажется, я могу получить только 256 байтов данных, остальные не записываются и обнуляются. Сжатый поток (ressedSection) был проверен, и все данные там (1500+ байтов). Ниже приведен фрагмент процесса декомпрессии:

int msiBuffer = 4096;
using (Stream msi = new MemoryStream(msiBuffer))
{
    msi.Write(compressedSection, 0, compressedSection.Length);
    msi.Position = 0;
    int uncompressedIntSize = AllMethods.GetLittleEndianInt(uncompressedSize, 0); // Gets little endian value of uncompressed size into an integer

    // SharpZipLib GZip method called
    using (GZipInputStream decompressStream = new GZipInputStream(msi, uncompressedIntSize))
    {
        using (MemoryStream outputStream = new MemoryStream(uncompressedIntSize))
        {
            byte[] buffer = new byte[uncompressedIntSize];
            decompressStream.Read(buffer, 0, uncompressedIntSize); // Stream is decompressed and read         
            outputStream.Write(buffer, 0, uncompressedIntSize);
            using (var fs = new FileStream(kernelSectionUncompressed, FileMode.Create, FileAccess.Write))
            {
                fs.Write(buffer, 0, buffer.Length);
                fs.Close();
            }
            outputStream.Close();
        }
        decompressStream.Close();

Итак, в этом фрагменте:

1) Сжатый раздел передан, готов к распаковке.

2) Ожидаемый размер несжатого вывода (который хранится в заголовке с файлом как 2-байтовое значение с прямым порядком байтов) передается через метод для преобразования его в целое число. Заголовок удаляется ранее, поскольку он не является частью сжатого файла GZIP.

3) Поток GZIP SharpLibZip объявляется с потоком сжатого файла (msi) и буфером, равным int uncompressedIntSize (протестировано со статическимтакже значение 4096).

4) Я настроил MemoryStream для обработки записи вывода в файл, так как GZipInputStream не имеет Read / Write;он принимает ожидаемый размер распакованного файла в качестве аргумента (емкость).

5) Для чтения / записи потока необходим массив byte [] в качестве первого аргумента, поэтому я установил массив byte [] с достаточным количествомпространство для взятия всех байтов распакованного вывода (3584 байта в данном случае, полученного из uncompressedIntSize).

6) int GzipInputStream декомпрессия Stream использует .Read с буфером в качестве первого аргумента, из смещения 0, используя uncompressedIntSizeкак граф. При проверке приведенных здесь аргументов буферный массив по-прежнему имеет емкость 3584 байта, но ему предоставлено только 256 байтов данных. Остальные нули.

Похоже, что вывод .Read сокращен до 256 байт, но я не уверен, где. Есть ли что-то, что я пропустил в Streams, или это ограничение .Read?

Ответы [ 2 ]

2 голосов
/ 09 октября 2019

Вам нужно зациклить при чтении из потока; lazy путь, вероятно, таков:

decompressStream.CopyTo(outputStream);

(но это не гарантирует остановку после uncompressedIntSize байтов - он попытается прочитать до конца decompressStream)

Более ручная версия (которая учитывает наложенный предел длины) будет:

const int BUFFER_SIZE = 1024; // whatever
var buffer = ArrayPool<byte>.Shared.Rent(BUFFER_SIZE);
try
{
    int remaining = uncompressedIntSize, bytesRead;
    while (remaining > 0 && // more to do, and making progress
        (bytesRead = decompressStream.Read(
        buffer, 0, Math.Min(remaining, buffer.Length))) > 0)
    {
        outputStream.Write(buffer, 0, bytesRead);
        remaining -= bytesRead;
    }
    if (remaining != 0) throw new EndOfStreamException();
}
finally
{
    ArrayPool<byte>.Shared.Return(buffer);
}
0 голосов
/ 10 октября 2019

Проблема оказалась недосмотром, который я сделал ранее в опубликованном коде:

Файл, с которым я работаю, имеет 27 разделов, которые GZipped, но у каждого из них есть заголовок, который сломаетсяраспаковка Gzip, если поток GZipInput достигает любого из них. При открытии базового файла он начинался с начала (корректируется на 6, чтобы избежать первого заголовка) каждый раз вместо перехода к следующему смещению после заголовка:

brg.BaseStream.Seek (6, SeekOrigin.Begin);

Вместо:

brg.BaseStream.Seek (absoluteSectionOffset, SeekOrigin.Begin);

Это означало, что извлеченные сжатые данные были амальгамойпервый раздел без заголовка + часть второго раздела вместе с его заголовком. Поскольку первый раздел имеет длину 256 байт без заголовка, эта часть правильно распаковывается потоком GZipInput. Но после этого получается 6-байтовый заголовок, который разбивает его, в результате чего остальная часть выходных данных равна 00.

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

...