一次功能需要接对百度统计dataApi,根据百度提供的PHP Demo,写golang的实现,但是老提示数据格式错误,数据已损坏,一路分析,解决了不少问题,其中对golang和php的gzip压缩结果不一样产生了疑问,各种求助,最终知道数据不一样是正常的。

首先看源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package main
import (
"bytes"
"compress/gzip"
"fmt"
)
func main() {
data := "a"
buffer := new(bytes.Buffer)
w, _ := gzip.NewWriterLevel(buffer, 9) //php: gzencode($json_data,9)
defer w.Close()
w.Write([]byte(data))
w.Flush()
fmt.Println(buffer.Bytes()[:])
}

不管是哪种语言的压缩,其实基本上都是基于标准的,而Golang和PHP都是基于RFC 1952 ,gzip数据格式如下:

1
2
3
4
5
+---+---+---+---+---+---+---+---+---+---+
|ID1|ID2|CM |FLG| MTIME |XFL|OS | (more-->)
+---+---+---+---+---+---+---+---+---+---+

再对比PHP和GoLang的头部定义

-PHPGo
D13131
D2139139
CM (compression method)88
FLG (flags)00
MTIME (modification time)0 0 0 00 9 110 136
XFL (extra flags)00
OS (operating system)0255

上面看到,Go 设置的头信息和PHP有部分差役,Go都设置了MTime(修改时间) 和OS(操作系统,为255 ,不知道代表什么意思),而PHP中的OS=0表示是 FAT文件系统。

有差役是正常的,重要的是能相互解压出数据

不同语言,默认的压缩级别不一样,也会导致压缩结果不一样,Go中默认级别是-1(DefaultCompression)。

1
2
3
4
func NewWriter(w io.Writer) *Writer {
z, _ := NewWriterLevel(w, DefaultCompression)
return z
}

虽然我们设置了和PHP中一样的压缩级别,但是结果依旧可能还是不一样,这是因为GZIP 软件核心算法deflate,是LZ77和Huffman压缩的结合,所使用的频率树来决定输出码。

最重要的压缩数据能够被正确的解压

不同语言,不同算法,结果有所不同,但最重要的是,数据能够被解压,再不同语言间相互解压,则就满足了,所以不需要太关注为什么Go和PHP的Gzip压缩结果不一样。