Golang Gzip压缩与解压(附坑)
Gzip是一种压缩文件格式并且也是一个在类Unix 上的一种文件解压缩的软件,通常指GNU計劃的實現,此處的gzip代表GNU zip。 也經常用來表示gzip這種文件格式。 軟件的作者是Jean-loup Gailly和Mark Adler。
演示
1package main
2
3import (
4 "bytes"
5 "compress/gzip"
6 "fmt"
7 "io/ioutil"
8)
9
10func main() {
11
12 var (
13 result bytes.Buffer
14 data = []byte("test")
15 )
16
17 // 压缩
18 w := gzip.NewWriter(&result)
19 w.Write(data)
20 //w.Flush()
21 w.Close()
22 fmt.Println("gzip size:", len(result.Bytes()))
23
24 // 解压
25 r, _ := gzip.NewReader(&result)
26 r.Close()
27 undatas, _ := ioutil.ReadAll(r)
28 fmt.Println("ungzip size:", len(undatas))
29}
输出:
1gzip size: 28
2ungzip size: 4
踩到的坑
通常情况下,在实际应用时演示代码中第20行的w.Close()加上defer是没有问题的,但存在有特殊情况。
1func Compression(data []byte) ([]byte, error) {
2 var result bytes.Buffer
3
4 gz := gzip.NewWriter(&result)
5 defer gz.Close()
6
7 if _, err := gz.Write(data); err != nil {
8 return nil, err
9 }
10
11 // 注释掉上面的defer gz.Close(),去除下面行的注释
12 // gz.Close()
13
14 return result.Bytes(), nil
15}
如上示例代码,在调用后没有返回错误的情况下,取其结果进行解压,将会喜获unexpected EOF错误。导致问题的罪魁祸首正是defer,当gz.Close()被执行时会向数据末尾写入EOF结束标志,但我们将gz.Close()放到defer中执行时,由于defer是在return确定返回值之后执行,也就导致gz.Close实际没能将EOF写入数据末尾。
解决方法很简单,不要在defer中调用close即可。
实例
1package utils
2
3import (
4 "bytes"
5 "compress/gzip"
6 "io/ioutil"
7)
8
9func Compression(data []byte) ([]byte, error) {
10 var result bytes.Buffer
11
12 gz := gzip.NewWriter(&result)
13
14 if _, err := gz.Write(data); err != nil {
15 return nil, err
16 }
17
18 if err := gz.Close(); err != nil {
19 return nil, err
20 }
21
22 return result.Bytes(), nil
23}
24
25func UnCompression(data []byte) ([]byte, error) {
26 dataTmp := bytes.NewReader(data)
27
28 r, err := gzip.NewReader(dataTmp)
29 if err != nil {
30 return nil, err
31 }
32 if err = r.Close(); err != nil {
33 return nil, err
34 }
35
36 result, err := ioutil.ReadAll(r)
37 if err != nil {
38 return nil, err
39 }
40
41 return result, nil
42}
附注
进行解压操作时,在gzip.NewReader()之后可以直接调用Close(),它不会关闭底层的io.Reader,不影响后续读取数据。