我正试图使用GZipStream写一些应用程序跟踪信息(它们在生产环境中往往会变得非常巨大)。因此,在这种情况下,我需要能够使用GZipStream打开现有文件并附加内容。所有事情看起来都很顺利,直到我们尝试解压缩该文件。似乎在解压缩GZipStream时,它仅读取了第一块数据,然后就像达到了EOF一样行为(即使文件包含了更多数据)。奇怪的是,当使用Windows或Winrar打开文件时,所有数据都似乎存在并被正确提取。
之前有人遇到过这个问题吗?
我花了很长时间才弄清楚这个问题。标准的C#实现GZipStream有一个缺陷,它不支持连接的gzip文件。它只会解压缩从连接创建的gzip文件的第一部分,并在此之后报告流结束。
下面是一个适用于连接的gzip文件的示例:
new StreamReader(new ICSharpCode.SharpZipLib.GZip.GZipInputStream(Console.OpenStandardInput()));
您可以从Nuget获取所需的库。我推荐使用JetBrains版本,即JetBrains.SharpZLib.Stripped。在.NET Core项目中尝试使用其他选项时,Nuget会抛出错误。
using System.IO;
using System.IO.Compression;
namespace GzipStuff;
public static class GzipFrameworkReader
{
private const byte GzipPreamble1 = 0x1f;
private const byte GzipPreamble2 = 0x8b;
private const byte GzipPreamble3 = 0x08;
public static string ReadFile(string path)
{
int marker = 0;
int b;
using FileStream fs = File.OpenRead(path);
MemoryStream outmem = new();
while ((b = fs.ReadByte()) != -1)
{
if (marker == 0 && (byte)b == GzipPreamble1)
{
marker++;
continue;
}
if (marker == 1)
{
if ((byte)b == GzipPreamble2)
{
marker++;
continue;
}
marker = 0;
}
if (marker == 2)
{
marker = 0;
if ((byte)b == GzipPreamble3)
{
AppendBytes(path, outmem, fs.Position - 3);
}
}
}
outmem.Seek(0, SeekOrigin.Begin);
using StreamReader reader = new(outmem);
return reader.ReadToEnd();
}
private static void AppendBytes(string path, MemoryStream outmem, long pos)
{
using FileStream substream = File.OpenRead(path);
substream.Seek(pos, SeekOrigin.Begin);
using GZipStream gzip = new(substream, CompressionMode.Decompress);
gzip.CopyTo(outmem);
}
}