周末需要用 C# 访问某网站接口,解析返回数据时发现有些不对,DeflateStream 解不开 zlib 压缩返回来的数据!略一搜索之下发现是个陈年老坑了。

三种格式

这里一共有三种格式,分别是:

  • RFC 1951 Raw Deflate
  • RFC 1950 Raw Deflate wrapped in zlib header and zlib trailer
  • RFC 1952 Raw Deflate wrapped in Gzip header and Gzip trailer

System.IO.Compression.DeflateStream 读写的格式被称为 Raw Deflate,虽然同样调用 zlib 库生成,但不包括任何头尾部分。
根据 RFC 1950zlib header 的定义如下:

   0   1
 +---+---+
 |CMF|FLG|   (-->)
 +---+---+

CMF:
  bits 0 to 3  CM     Compression method
  bits 4 to 7  CINFO  Compression info

FLG:
  bits 0 to 4  FCHECK  (check bits for CMF and FLG)
  bit  5       FDICT   (preset dictionary)
  bits 6 to 7  FLEVEL  (compression level)

常见的几种组合:

78 01 - No Compression/low
78 9C - Default Compression
78 DA - Best Compression

其中 78 9C 尤为常见。

解决方案

1. 解压缩、格式固定

这也是我这次遇到的场景。
远端服务器(目测是 nodejs)提供 JSON 序列化的内容,其中一个字段是 binary string,处理之后得到一个 zlib 压缩的字节序列。
这种情况下,直接 Skip(2) 再丢给 DeflateStream 解压就可以了,毕竟官方库的性能是最好的

2. 解压缩、算法固定、格式兼容

建议放弃官方库,使用 SharpZipLib 等第三方库。

3. 纯压缩、提供给前端下载

建议考虑其他压缩格式,如 Zip 等。

4. 纯压缩、与其他服务器交互

建议优先约定 Raw Deflate,无法协商一致或对端要求 RFC 1950RFC 1952,可以在以下两种方式中二选一:

  • 自己造轮子,补足对应的 headertrailer
  • 使用 SharpZipLib 等第三方库

如果考虑性能,可能自行补完头尾更合适一些,具体方式可以参考 RFC。

参考