Golang:“compress/flate”模块无法解压有效的 deflate 压缩 HTTP 主体

问题描述 投票:0回答:2

这个问题延续了这里开始的讨论。我发现 HTTP 响应正文无法解组为 JSON 对象,因为后者进行了

deflate
压缩。现在我想知道如何用Golang进行解压。我将不胜感激任何能够显示我的代码中的错误的人。

输入数据

我已将 HTTP 响应正文转储到“test”文件中。这是它:

$ cat test
x��PAN�0�
;��NtJ�FӮdU�|"oVR�C%�f�����Z.�^Hs�dW뮑�'��DH�S�SFVC����r)G,�����<���z}�x_g�+�2��sl�r/�Oy>��J3\�G�9���N���#[5M�^v/�2Ҕ��|�h��[�~7�_崛<D*���/��i

让我们确保该文件可以解压,甚至包含有效的 JSON:

$ zlib-flate -uncompress < test
{"timestamp":{"tv_sec":1428488670,"tv_usec":197041},"string_timestamp":"2015-04-08 10:24:30.197041","monitor_status":"enabled","commands":{"REVERSE_LOOKUP":{"cache":{"outside":{"successes":0,"failures":0,"size":0,"time":0},"internal":{"successes":0,"failures":0,"size":0,"time":0}},"disk":{"outside":{"successes":0,"failures":0,"size":0,"time":0},"internal":{"successes":13366,"failures":0,"size":0,"time":501808}},"total":{"storage":{"successes":0,"failures":0},"proxy":{"successes":13366,"failures":0}}},"clients":{}}}
$ zlib-flate -uncompress < test | python -m json.tool
{
    "commands": {
        "REVERSE_LOOKUP": {
            "cache": {
               ....

源代码

package main

import (
    "bytes"
    "compress/flate"
    "fmt"
    "io/ioutil"
)

func main() {
    fname := "./test"
    content, err := ioutil.ReadFile(fname)
    if err != nil {
        panic(err)
    }
    fmt.Println("File content:\n", content)

    enflated, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(content)))
    if err != nil {
        panic(err)
    }
    fmt.Println("Enflated:\n", enflated)
}

错误

$ go run uncompress.go 
File content:
 [120 156 181 80 65 78 195 48 16 252 10 242 57 69 118 226 166 38 247 156 64 42 42 130 107 100 156 165 88 196 118 149 93 35 160 234 223 89 183 61 112 42 226 192 109 118 118 102 103 180 123 65 62 0 146 13 59 209 237 5 189 15 8 78 116 74 215 70 27 211 174 100 85 184 124 34 111 86 82 171 67 37 144 102 31 183 195 15 167 168 165 90 46 164 94 72 115 165 100 87 235 174 145 215 39 189 168 68 72 209 83 154 7 22 83 70 86 67 180 207 19 140 188 114 41 4 27 71 44 225 155 254 169 223 60 244 195 221 122 125 251 120 95 24 103 221 43 20 144 50 161 31 143 16 179 115 128 8 108 225 114 47 214 79 121 62 15 232 191 224 8 74 51 6 92 213 71 130 57 218 233 175 78 182 142 30 223 254 35 91 53 77 219 94 118 47 165 50 210 148 18 148 232 124 128 31 104 183 151 91 176 126 55 167 143 207 95 3 15 229 180 155 60 68 42 159 231 241 27 47 165 167 25]
panic: flate: corrupt input before offset 5

goroutine 1 [running]:
runtime.panic(0x4a7180, 0x5)
    /usr/lib/go/src/pkg/runtime/panic.c:266 +0xb6
main.main()
    /home/isaev/side-projects/elliptics-manager/uncompress.go:20 +0x2a3
exit status 2

PSUbuntu 14.10,Go 1.2.1

go zlib deflate
2个回答
15
投票

您的输入不是一个简单的压缩块,而是一个 zlib 流

根据ZLIB压缩数据格式规范3.3前2个字节是:

-------------
| CMF | FLG |
-------------

压缩方法和标志。您的输入以

[120, 156]
开头,即十六进制的
78 9C
。这是默认压缩。后面也没有字典,所以后面的数据是压缩数据。

位 0 到 3 是 CM 压缩方法,位 4 到 7 是 CINFO 压缩信息。在这种情况下,CINFO=7 表示 32K 窗口大小,CM=8 表示“deflate”压缩方法。 FLG 位 5 指示是否预设了字典,在本例中就是这样。 FLG 的详细信息也在链接的 RFC 1950 中。

因此,您的输入基本上表明其余数据是使用默认压缩构建的,但 go

flate
包不会对此进行解码。

更改解压方式以省略前 2 个字节,如下所示,它将起作用:

enflated, err := ioutil.ReadAll(flate.NewReader(bytes.NewReader(content[2:])))

Go Playground上尝试一下。但是...

使用正确的ZLib解压!

这次我们很幸运,因为压缩级别是默认的,并且字典是预设的。如果没有,您将无法使用

flate
包对其进行解码。由于输入是 zlib 流,因此您应该使用
compress/zlib
包来正确解码它,而不是依靠运气:

r, err := zlib.NewReader(bytes.NewReader(content))
if err != nil {
    panic(err)
}
enflated, err := ioutil.ReadAll(r)
if err != nil {
    panic(err)
}
fmt.Println(string(enflated))

尝试

Go Playground
上的 zlib 变体。


0
投票

据我所知,

compress/flate
包也适用于以相同格式编码数据。

© www.soinside.com 2019 - 2024. All rights reserved.