想要改善这篇文章吗? 提供此问题的详细答案,包括引文和为什么答案正确的解释。答案不够详细的答案可能会被编辑或删除。
如果我压缩此文本:
你好,世界
通过C#使用以下代码:
Stream stream = new MemoryStream(Encoding.Default.GetBytes("Hello World")); var compressedMemoryStream = new MemoryStream(); using (var gzipStream = new GZipStream(compressedMemoryStream, CompressionMode.Compress)) { stream.CopyTo(gzipStream); gzipStream.Close(); }
结果流为133个字节长
通过Fiddler Utilities.GzipCompress或此PHP页面运行相同的字符串,结果只有31个字节长。
Utilities.GzipCompress
在这两种情况下,输入均为11个字节,因此我可以想象PHP结果是正确的,但是显然这意味着我无法从.NET内解压缩PHP zip,反之亦然。为什么.NET输出这么大?
实际上,事实证明,虽然PHP和Fiddler的结果长度相同,但它们却不同。我可以解压缩.NET中的PHP版本,但不能解压缩Fiddler版本。PHP页面对这三个页面都进行了解压缩,因此Fiddler和.NET的gzip实现之间似乎不兼容。
按照要求,我上传的三个输出到Dropbox的位置。
这些是这些文件的原始十六进制转储(不知道它们是否真的像这样使用,但是我认为这表明fiddler和PHP版本之间的区别在于标头,而不是压缩数据本身):
提琴手:
0000-0010: 1f 8b 08 00-c2 e6 ff 4f-00 ff f3 48-cd c9 c9 57 .......O ...H...W 0000-001f: 08 cf 2f ca-49 01 00 56-b1 17 4a 0b-00 00 00 ../.I..V ..J....
PHP:
0000-0010: 1f 8b 08 00-00 00 00 00-00 03 f3 48-cd c9 c9 57 ........ ...H...W 0000-001f: 08 cf 2f ca-49 01 00 56-b1 17 4a 0b-00 00 00 ../.I..V ..J....
C#:
0000-0010: 1f 8b 08 00-00 00 00 00-04 00 ec bd-07 60 1c 49 ........ .....`.I 0000-0020: 96 25 26 2f-6d ca 7b 7f-4a f5 4a d7-e0 74 a1 08 .%&/m.{. J.J..t.. 0000-0030: 80 60 13 24-d8 90 40 10-ec c1 88 cd-e6 92 ec 1d .`.$..@. ........ 0000-0040: 69 47 23 29-ab 2a 81 ca-65 56 65 5d-66 16 40 cc iG#).*.. eVe]f.@. 0000-0050: ed 9d bc f7-de 7b ef bd-f7 de 7b ef-bd f7 ba 3b .....{.. ..{....; 0000-0060: 9d 4e 27 f7-df ff 3f 5c-66 64 01 6c-f6 ce 4a da .N'...?\ fd.l..J. 0000-0070: c9 9e 21 80-aa c8 1f 3f-7e 7c 1f 3f-22 be 9d 97 ..!....? ~|.?"... 0000-0080: 65 95 7e b7-aa cb d9 ff-13 00 00 ff-ff 56 b1 17 e.~..... .....V.. 0000-0085: 4a 0b 00 00-00
前言: .NET用户在任何情况下都不应使用Microsoft提供的GZipStream或DeflateStream类,除非Microsoft用可行的东西完全替换了它们。 请改用 DotNetZip库。
更新到前言: .NET Framework 4.5和更高版本已解决了压缩问题,并且GZipStream和DeflateStream在这些版本中使用zlib。我不知道下面提到的CRC问题是否已解决。
另一个更新: CRC问题不仅没有得到解决,而且Microsoft决定他们不会解决它!
这是GZipStream中的几个错误之一。自尊的gzip压缩器 永远 都不能从11个字节的输入中产生133个字节的输出。请参阅为什么BCL GZipStream(使用StreamReader)不能可靠地使用CRC32检测数据错误的评论。。
内部发生的事情是GZipStream没有使用静态或存储方法,这两种方法都将产生与输入数据大小相同的压缩数据(在其顶部将添加18个字节的gzip标头和尾标)。相反,它使用了动态方法,该方法为非常少量的代码创建了非常大的代码描述符标头。这仅仅是一个错误/非常糟糕的实现。
更新:
对于十六进制转储,我可以提供一些分析。首先,Fiddler和php输出都是正确和正确的。它们之间的唯一区别是在gzip标头中,特别是在Fiddler中设置的时间戳(而不是在php中),以及原始操作系统在php中设置,而不是在Fiddler中。因为这13个字节的压缩数据是相同的,并且可以表示为(使用我的infgen程序反汇编deflate流):
last static literal 'Hello World end
完全应该如此。单个静态块,不需要代码描述符,只需将所有字节编码为文字。(没有与长度和距离匹配的先前字符串。)
另一方面,GZipStream的输出在几种方面令人震惊。压缩后的数据为:
dynamic code 3 5 code 4 5 code 5 4 code 6 4 code 7 4 code 8 3 code 9 3 code 10 4 code 11 4 code 12 4 code 13 4 code 14 3 code 16 3 litlen 0 14 litlen 1 14 litlen 2 14 litlen 3 14 litlen 4 14 litlen 5 14 litlen 6 14 litlen 7 14 litlen 8 14 litlen 9 12 litlen 10 6 litlen 11 14 litlen 12 14 litlen 13 14 litlen 14 14 litlen 15 14 litlen 16 14 litlen 17 14 litlen 18 14 litlen 19 14 litlen 20 14 litlen 21 14 litlen 22 14 litlen 23 14 litlen 24 14 litlen 25 14 litlen 26 14 litlen 27 14 litlen 28 14 litlen 29 14 litlen 30 13 litlen 31 14 litlen 32 6 litlen 33 14 litlen 34 10 litlen 35 12 litlen 36 14 litlen 37 14 litlen 38 13 litlen 39 10 litlen 40 8 litlen 41 9 litlen 42 11 litlen 43 10 litlen 44 7 litlen 45 8 litlen 46 7 litlen 47 9 litlen 48 8 litlen 49 8 litlen 50 8 litlen 51 9 litlen 52 8 litlen 53 9 litlen 54 10 litlen 55 9 litlen 56 8 litlen 57 9 litlen 58 9 litlen 59 8 litlen 60 9 litlen 61 10 litlen 62 8 litlen 63 14 litlen 64 14 litlen 65 8 litlen 66 9 litlen 67 8 litlen 68 9 litlen 69 8 litlen 70 9 litlen 71 10 litlen 72 11 litlen 73 8 litlen 74 11 litlen 75 14 litlen 76 9 litlen 77 10 litlen 78 9 litlen 79 10 litlen 80 9 litlen 81 12 litlen 82 9 litlen 83 9 litlen 84 9 litlen 85 10 litlen 86 12 litlen 87 11 litlen 88 14 litlen 89 14 litlen 90 12 litlen 91 11 litlen 92 14 litlen 93 11 litlen 94 14 litlen 95 14 litlen 96 14 litlen 97 6 litlen 98 7 litlen 99 7 litlen 100 7 litlen 101 6 litlen 102 8 litlen 103 8 litlen 104 7 litlen 105 6 litlen 106 12 litlen 107 9 litlen 108 6 litlen 109 7 litlen 110 7 litlen 111 6 litlen 112 7 litlen 113 13 litlen 114 6 litlen 115 6 litlen 116 6 litlen 117 7 litlen 118 8 litlen 119 8 litlen 120 9 litlen 121 8 litlen 122 11 litlen 123 13 litlen 124 12 litlen 125 13 litlen 126 13 litlen 127 14 litlen 128 14 litlen 129 14 litlen 130 14 litlen 131 14 litlen 132 14 litlen 133 14 litlen 134 14 litlen 135 14 litlen 136 14 litlen 137 14 litlen 138 14 litlen 139 14 litlen 140 14 litlen 141 14 litlen 142 14 litlen 143 14 litlen 144 14 litlen 145 14 litlen 146 14 litlen 147 14 litlen 148 14 litlen 149 14 litlen 150 14 litlen 151 14 litlen 152 14 litlen 153 14 litlen 154 14 litlen 155 14 litlen 156 14 litlen 157 14 litlen 158 14 litlen 159 14 litlen 160 14 litlen 161 14 litlen 162 14 litlen 163 14 litlen 164 14 litlen 165 14 litlen 166 14 litlen 167 14 litlen 168 14 litlen 169 14 litlen 170 14 litlen 171 14 litlen 172 14 litlen 173 14 litlen 174 14 litlen 175 14 litlen 176 14 litlen 177 14 litlen 178 14 litlen 179 14 litlen 180 14 litlen 181 14 litlen 182 14 litlen 183 14 litlen 184 14 litlen 185 14 litlen 186 14 litlen 187 14 litlen 188 14 litlen 189 14 litlen 190 14 litlen 191 14 litlen 192 14 litlen 193 14 litlen 194 14 litlen 195 14 litlen 196 14 litlen 197 14 litlen 198 14 litlen 199 14 litlen 200 14 litlen 201 14 litlen 202 14 litlen 203 14 litlen 204 14 litlen 205 14 litlen 206 14 litlen 207 14 litlen 208 14 litlen 209 14 litlen 210 14 litlen 211 14 litlen 212 14 litlen 213 14 litlen 214 14 litlen 215 14 litlen 216 14 litlen 217 14 litlen 218 14 litlen 219 14 litlen 220 14 litlen 221 14 litlen 222 14 litlen 223 14 litlen 224 14 litlen 225 14 litlen 226 14 litlen 227 14 litlen 228 14 litlen 229 14 litlen 230 14 litlen 231 14 litlen 232 14 litlen 233 14 litlen 234 14 litlen 235 14 litlen 236 14 litlen 237 14 litlen 238 14 litlen 239 14 litlen 240 14 litlen 241 14 litlen 242 14 litlen 243 13 litlen 244 13 litlen 245 13 litlen 246 14 litlen 247 13 litlen 248 14 litlen 249 13 litlen 250 14 litlen 251 13 litlen 252 14 litlen 253 14 litlen 254 14 litlen 255 14 litlen 256 14 litlen 257 4 litlen 258 3 litlen 259 4 litlen 260 4 litlen 261 4 litlen 262 5 litlen 263 5 litlen 264 5 litlen 265 5 litlen 266 5 litlen 267 6 litlen 268 6 litlen 269 5 litlen 270 6 litlen 271 7 litlen 272 8 litlen 273 8 litlen 274 9 litlen 275 10 litlen 276 9 litlen 277 10 litlen 278 12 litlen 279 11 litlen 280 12 litlen 281 14 litlen 282 14 litlen 283 14 litlen 284 12 litlen 285 11 dist 0 6 dist 1 10 dist 2 11 dist 3 11 dist 4 9 dist 5 8 dist 6 8 dist 7 8 dist 8 7 dist 9 7 dist 10 5 dist 11 6 dist 12 4 dist 13 5 dist 14 4 dist 15 5 dist 16 4 dist 17 5 dist 18 4 dist 19 4 dist 20 4 dist 21 4 dist 22 4 dist 23 4 dist 24 4 dist 25 5 dist 26 4 dist 27 5 dist 28 5 dist 29 5 literal 'Hello World end ! last stored end
那到底是什么?实际数据只是“ literal’Hello World”末尾附近的行,该行仅对输入的每个字节进行编码。它之前是一组有关文字,长度和距离的霍夫曼代码的描述。这是问题所在:
所有这一切都表明一个简单的事实,就是谁写了这个GZipStream代码,都是出于礼貌地对一般的deflate格式或压缩不了解。他们选择只产生动态块(末尾有一个空的静态块除外),每次都只产生相同的动态头(我认为),这违背了动态块的目的,并且不去理会当前块是最后一个,需要放一个空块来标记结尾。
如其他地方所述,这些并不是GZipStream的唯一问题。它甚至无法正确地使用CRC-32来检测损坏的流。
真正令人困惑的不是为什么Microsoft指派不称职的人来编写gzip压缩器和解压缩器,而是为什么他们要指派任何人来编写它!有免费的代码zlib,它具有极其宽松的许可证,允许在没有署名的情况下进行商业使用。这段代码已经广泛部署了近二十年,它可以正确有效地完成所有工作。大多数其他东西都使用zlib,包括php,我也怀疑Fiddler。