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