使用GZipStream将数据附加到压缩文件

5
我正试图使用GZipStream写一些应用程序跟踪信息(它们在生产环境中往往会变得非常巨大)。因此,在这种情况下,我需要能够使用GZipStream打开现有文件并附加内容。所有事情看起来都很顺利,直到我们尝试解压缩该文件。似乎在解压缩GZipStream时,它仅读取了第一块数据,然后就像达到了EOF一样行为(即使文件包含了更多数据)。奇怪的是,当使用Windows或Winrar打开文件时,所有数据都似乎存在并被正确提取。 之前有人遇到过这个问题吗?

可能是向压缩流追加数据的重复问题。 - Adrian Zanescu
3个回答

2
我遇到了同样的问题。 想法是实现类似于http://zlib.net/pigz/所做的东西。
想法是删除旧gzip块的最后8个字节(页脚),从页脚中提取CRC和大小,然后添加一些零,然后追加新块,然后根据旧块和新块的大小和CRC重新计算源大小和CRC,并替换结果页脚。问题在于我没有找到如何基于两部分CRC生成有效的CRC总和。此外,新块需要先删除其头部。 pigz所做的也是在块之间共享一些字典数据,并且它执行上述所有操作,因此您可以查看源代码。

3
我可以翻译成中文,您希望编辑并说明一下zlib在这个上下文中的作用,以及如何应用它。目前的形式除了“转到该链接”之外没有提供太多信息,如果您愿意做出修改并解释一下,我会接受这份文件 :) - 毕竟并没有太多其他选择。 - Adrian Zanescu
我并不是追求接受和积分,只是喜欢SOF居民互相帮助的方式 :) 这个想法是删除旧gzip块的最后8个字节(页脚),从页脚中提取CRC和大小,然后添加一些零,然后附加新块,然后根据旧块和新块的大小和CRC重新计算源大小和CRC,并替换结果页脚。问题在于我没有找到如何基于两个部分的CRC生成有效的总和CRC。此外,新块需要先删除其标头。 - Igor Be
pigz 的作用还包括在chunk之间共享一些字典数据,并且它执行了上述所有的操作,因此你可以查看源代码。如果您在 .NET 中实现了这一点,请告诉我,我已经放弃了这种方法并添加了分块分隔符和阅读器以提供按块提供数据解压缩。 - Igor Be

2

我花了很长时间才弄清楚这个问题。标准的C#实现GZipStream有一个缺陷,它不支持连接的gzip文件。它只会解压缩从连接创建的gzip文件的第一部分,并在此之后报告流结束。

下面是一个适用于连接的gzip文件的示例:

new StreamReader(new ICSharpCode.SharpZipLib.GZip.GZipInputStream(Console.OpenStandardInput()));

您可以从Nuget获取所需的库。我推荐使用JetBrains版本,即JetBrains.SharpZLib.Stripped。在.NET Core项目中尝试使用其他选项时,Nuget会抛出错误。


0
从一个带有附加内容的Gzip文件中读取只在.NET Framework中存在问题。解决方法是读取文件流,寻找Gzip的魔术字节,并从这些偏移量开始打开子流。解决方案明显效率低下,但可行。
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);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接