C# MemoryStream & GZipInputStream: Can't .读取超过 256 字节



我在使用 SharpZipLib 的 GZipInputStream 编写未压缩的 GZIP 流时遇到问题。我似乎只能获得 256 字节的数据,其余的没有写入并归零。压缩流(压缩部分(已被检查,所有数据都在那里(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(SharpLibZip的GZIP流是用压缩文件流(msi(和一个等于int uncompressedIntSize的缓冲区声明的(也测试过静态值4096(。

4(我设置了一个内存流来处理将输出写入文件,因为GZipInputStream没有读/写;它将预期的解压缩文件大小作为参数(容量(。

5( 流的读/写需要 byte[] 数组作为第一个参数,所以我设置了一个 byte[] 数组,该数组有足够的空间来获取解压缩输出的所有字节(在本例中为 3584 字节,源自未压缩的 IntSize(。

6( int GzipInputStream 解压缩流使用 .使用缓冲区作为第一个参数从偏移量 0 读取,使用未压缩的 IntSize 作为计数。检查此处的参数,缓冲区数组的容量仍为 3584 字节,但仅给定 256 字节的数据。其余的都是零。

它看起来像 的输出。读取被限制为 256 字节,但我不确定在哪里。流是否遗漏了某些内容,或者这是 的限制。读?

从流中读取时需要循环;懒惰的方式可能是:

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);
}

这个问题原来是我之前在发布的代码中所做的疏忽:

我正在使用的文件有 27 个部分是 GZipped,但它们每个都有一个标头,如果 GZipInput 流命中其中任何一个,它将破坏 Gzip 解压缩。打开基本文件时,它每次都从头开始(调整 6 以避免第一个标头(,而不是转到下一个后头部偏移量:

BRG.BaseStream.Seek(6, SeekOrigin.Begin(;

而不是:

BRG.BaseStream.Seek(AbsoluteSectionOffset, SeekOrigin.Begin(;

这意味着提取的压缩数据是第一个无标题部分 + 第二个部分的一部分及其标题的混合体。由于第一部分的长度为 256 字节,没有其标头,因此这部分被 GZipInput 流正确解压缩。但之后是 6 字节的标头,这会破坏它,导致输出的其余部分为 00s。

发生这种情况时,GZipInput 流没有引发显式错误,因此我错误地认为原因是 .读取或流中的某些内容保留上一次传递的数据。很抱歉给您带来麻烦。

最新更新