Golang:"压缩/ flate"模块无法解压缩有效的deflate压缩HTTP正文

时间:2015-04-08 11:35:06

标签: go zlib deflate

这个问题继续讨论开始here。我发现由于后者的deflate压缩,HTTP响应主体无法解组为JSON对象。现在我想知道如何使用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

PS Ubuntu 14.10,Go 1.2.1

1 个答案:

答案 0 :(得分:8)

您的输入不是简单的缩减块,它是 zlib流

根据ZLIB Compressed Data Format Specification 3.3,前2个字节是:

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

压缩方法和标志。您的输入以[120, 156]开头,其中78 9C为hexa。这是默认压缩。此外,没有字典,因此后续数据是压缩数据。

位0到3是CM压缩方法,位4到7是CINFO压缩信息。在这种情况下,CINFO = 7表示32K窗口大小,CM = 8表示&#34; deflate&#34;压缩方法。 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变体。