在压缩数据字节数组的末尾添加一些垃圾字节后,是不是可以使用 GZIP 解压缩数据?

Posted

技术标签:

【中文标题】在压缩数据字节数组的末尾添加一些垃圾字节后,是不是可以使用 GZIP 解压缩数据?【英文标题】:Is it possible to decompress data using GZIP, after some garbage-bytes are added to the end of compressed data byte array?在压缩数据字节数组的末尾添加一些垃圾字节后,是否可以使用 GZIP 解压缩数据? 【发布时间】:2020-09-10 01:17:56 【问题描述】:

这只是c#中的一个示例,我的意思是在末尾添加垃圾数据,请将其视为伪代码:

[Test]
public void TestGzipCompressor_WhenCompressCalledWithAddedExtraDataToTheEnd_ShouldReturnValidData()

    var extraBytesToAddToTheEnd = new byte[]  1, 2, 3, 4 ;
    
    //creating a test-byte array
    var bytesToPopulateArrayWith= new byte[9]  1, 2, 3, 4, 5, 6, 7, 8, 9 ;
    var byteList = new List<byte>  ;
    for (int i = 0; i < 100; i++)
    
        inputBytes.AddRange(bytesToPopulateArrayWith);
    
    
    //Getting data as a compressed byte list
    List<byte> compressedBytes = GzipCompressor.Compress(byteList);

    //adding extra garbage-bytes to the end of the compressed data byte-array
    compressedBytes.AddRange(extraBytesToAddToTheEnd);

    //Getting original data after decompression
    byte[] decompressedBytes = GzipCompressor.Decompress(compressedBytes.ToArray());

    decompressedBytes.Should().BeEquivalentTo(stringBytes);

如果我们添加少于 5 个字节,它就可以完美运行:

var extraBytesToAddToTheEnd = new byte[]  1, 2, 3, 4 ;

但在末尾添加 5 个或更多字节会引发“存档条目是使用不支持的压缩方法压缩的”错误。

var extraBytesToAddToTheEnd = new byte[]  1, 2, 3, 4, 5 ;

压缩器实现如下:

public static class GzipCompressor
    
        [NotNull]
        public static byte[] Compress([NotNull] byte[] bytes)
        
            using var memoryStream = new MemoryStream();
            using var gzipStream = new GZipStream(memoryStream, CompressionMode.Compress);
            gzipStream.Write(bytes, 0, bytes.Length);
            gzipStream.Flush();
            return memoryStream.ToArray();
        

        [NotNull]
        public static byte[] Decompress([NotNull] byte[] bytes)
        
            using var memoryStream = new MemoryStream();
            using var gzipStream = new GZipStream(new MemoryStream(bytes), CompressionMode.Decompress);
            gzipStream.CopyTo(memoryStream);
            gzipStream.Flush();
            return memoryStream.ToArray();
        
    

GZIP 是否可以解压缩最后带有一些垃圾字节的压缩数据? 为什么它可以使用 4 个字节或更少,但没有更多?我的实现有问题吗? 谢谢!

【问题讨论】:

你这里的用例是什么,为什么需要这样做?如果您需要在非常特定的文件格式的末尾对数据进行编码,为什么不添加数据,在末尾添加一个大小,当您去解压缩时,读取末尾的大小,删除数据,然后解压缩。至于为什么你可以逃脱 4 个字节,谁知道,你必须阅读 RFC,这可能有很多原因,比如填充,这实际上可能会因其他植入而失败...... 您应该阅读规范。如果您想添加自己的数据,您可以阅读有关如何执行此操作的规范,特别是 2.3.1.1 部分:tools.ietf.org/html/rfc1952#page-8 【参考方案1】:

如果不深入研究 GZipStream 实现,我猜它正在读取您附加的数据并尝试将其解释为 gzip 帧头,当它收到意外输入时会引发异常。

gzip 文件格式详见RFC 1952。它由一系列都遵循特定格式的框架(在 RFC 中称为“成员”)组成。每个帧都有一个带有一些可选部分的标题块,后面是 DEFLATE 压缩数据和一个 8 字节的页脚。该格式支持标头中可选的“额外数据”块,但大多数实现似乎都忽略了它们。

但是,文件格式不允许将任意数据附加或附加到文件中。通过将数据附加到 gzip 流,您将其更改为 不是 gzip 文件的内容。您创建了一个 gzip 变体,它不适用于合规的实现,并且在不合规的实现上具有未定义的行为。

根据您的需要,还有其他可能更适合的压缩格式。如果它是供内部使用的,那么您可能想要创建自己的格式来封装 gzip 或 deflate 数据。如果您绝对必须生成一个 gzip 文件,那么您需要找到一个允许您在 gzip 标头中读取/写入额外数据的库。

(希望我可以标记人 - Mark Adler 是 SO 上这些东西的权威来源。)

【讨论】:

【参考方案2】:

看起来像 GzipStream 中的错误。它直到四个字节之后才识别垃圾。正如 Corey 所指出的,gzip 成员之后的任何内容如果不是另一个完整的 gzip 成员,则表示无效的 gzip 流。该类应该识别出是否有五个、四个或一个字节的垃圾。

如果您想在一个 gzip 成员之后停止解压缩,您可以改用 DeflateStream 来解码 gzip 成员内的压缩数据。然后您只需要自己解码 gzip 标头和预告片,并计算解压缩数据的 CRC 以与预告片中的 CRC 进行比较。见RFC 1952。

【讨论】:

以上是关于在压缩数据字节数组的末尾添加一些垃圾字节后,是不是可以使用 GZIP 解压缩数据?的主要内容,如果未能解决你的问题,请参考以下文章

从 C# 中的字节数组中删除尾随空值

如何将字节数组附加到Go中的字节片[重复]

大字节数组 - 在字节数组中存储长度有啥好处?

PE格式自检

zlib 的“解压缩”是不是保留数据的原始字节序,还是进行字节序转换?

在C#中解压缩字节数组