文章目录
- 一:简介
- 二:Go标准库compress/gzip包介绍
-
- Constants
- Variables
- type Header
- type Reader
- 三:代码实践
-
- 1、压缩与解压工具包
- 2、单元测试
- 3、为何压缩后还要用base64编码
代码地址:
https://gitee.com/lymgoforIT/golang-trick/tree/master/41-go-gzip
一:简介
在工作中,我们有时候需要用到
在介绍
但有些场景要缓存的一条信息内容可能就是比较大,且无法拆分,这时候怎么办呢?那就压缩后再存入
二:Go标准库compress/gzip包介绍
compress/gzip是Go标准库中的一个压缩工具包,中文文档地址:https://studygolang.com/static/pkgdoc/pkg/compress_gzip.htm
-
Constants:常量定义
-
Variables:变量定义
-
type Header:数据头结构
-
type Reader:解压
- func NewReader(r io.Reader) (*Reader, error)
- func (z *Reader) Reset(r io.Reader) error
- func (z *Reader) Read(p []byte) (n int, err error)
- func (z *Reader) Close() error
-
type Writer:压缩
- func NewWriter(w io.Writer) *Writer
- func NewWriterLevel(w io.Writer, level int) (*Writer, error)
- func (z *Writer) Reset(w io.Writer)
- func (z *Writer) Write(p []byte) (int, error)
- func (z *Writer) Flush() error
- func (z *Writer) Close() error
Constants
const (
NoCompression = flate.NoCompression // 不压缩
BestSpeed = flate.BestSpeed // 最快速度
BestCompression = flate.BestCompression // 最佳压缩比
DefaultCompression = flate.DefaultCompression // 默认压缩比
)
这些常量都是拷贝自
Variables
var (
// 当读取的gzip数据的校验和错误时,会返回ErrChecksum
ErrChecksum = errors.New("gzip: invalid checksum")
// 当读取的gzip数据的头域错误时,会返回ErrHeader
ErrHeader = errors.New("gzip: invalid header")
)
type Header
//数据头结构
type Header struct {
Comment string // 文件注释
Extra []byte // 附加数据
ModTime time.Time // 文件修改时间
Name string // 文件名
OS byte // 操作系统类型
}
type Reader
type Reader struct {
Header
// 内含隐藏或非导出字段
}
一般情况下,一个
func NewReader
func NewReader(r io.Reader) (*Reader, error)
func (*Reader) Reset
func (z *Reader) Reset(r io.Reader) error
func (*Reader) Read
func (z *Reader) Read(p []byte) (n int, err error)
func (*Reader) Close
func (z *Reader) Close() error
调用
type Writer
type Writer struct {
Header
// 内含隐藏或非导出字段
}
func NewWriter
func NewWriter(w io.Writer) *Writer
如要设定
func NewWriterLevel
func NewWriterLevel(w io.Writer, level int) (*Writer, error)
参数
func (*Writer) Reset
func (z *Writer) Reset(dst io.Writer)
func (*Writer) Write
func (z *Writer) Write(p []byte) (int, error)
func (*Writer) Flush
func (z *Writer) Flush() error
本方法主要用在传输压缩数据的网络连接中,以保证远端的接收者可以获得足够的数据来重构数据报。
func (*Writer) Close
func (z *Writer) Close() error
调用
三:代码实践
1、压缩与解压工具包
package util
import (
"bytes"
"compress/gzip"
"context"
"encoding/base64"
"fmt"
)
// GzipEncode 采用gzip算法压缩字符串,输出base64编码的字符串
func GzipEncode(ctx context.Context, input string) (string, error) {
if len(input) == 0 {
return input, nil
}
var b bytes.Buffer // 实现了io.Writer
gz := gzip.NewWriter(&b)
defer func() {
if err := gz.Close(); err != nil {
fmt.Println(fmt.Sprintf("gz.Close() err:%v", err))
}
}()
if _, err := gz.Write([]byte(input)); err != nil {
fmt.Println(fmt.Sprintf("[GzipEncode] gz write err:%v", err))
return "", err
}
// 将gzip.Writer缓冲中的数据刷到底层io.Writer中
if err := gz.Flush(); err != nil {
fmt.Println(fmt.Sprintf("[GzipEncode] gz flush err:%v", err))
return "", err
}
// 在读取数据之前必须close,否则读取的数据会有问题,在这里作用同Flush一样
// 即将压缩后的数据立即写入底层io.Writer中,在这里是b(bytes.Buffer)
if err := gz.Close(); err != nil {
fmt.Println(fmt.Sprintf("[GzipEncode] gz close err:%v", err))
return "", err
}
newStr := base64.StdEncoding.EncodeToString(b.Bytes())
return newStr, nil
}
// GzipDecode 采用gzip算法解压字符串
func GzipDecode(ctx context.Context, input string) (string, error) {
newInput, err := base64.StdEncoding.DecodeString(input)
if err != nil {
fmt.Println(fmt.Sprintf("[GzipDecode] base decode err:%v", err))
return "", err
}
bReader := bytes.NewReader(newInput)
gReader, err := gzip.NewReader(bReader)
if err != nil {
fmt.Println(fmt.Sprintf("[GzipDecode] new reader err,%v", err))
return "", err
}
if err = gReader.Close(); err != nil {
fmt.Println(fmt.Sprintf("[GzipDecode] reader close err:%v", err))
return "", err
}
buf := new(bytes.Buffer)
if _, err = buf.ReadFrom(gReader); err != nil {
fmt.Println(fmt.Sprintf("[GzipDecode] read from greader err:%v", err))
return "", err
}
return buf.String(), err
}
2、单元测试
package util
import (
"context"
"fmt"
"testing"
)
func TestGzipEncode(t *testing.T) {
encode, err := GzipEncode(context.Background(), "hello world")
if err != nil {
fmt.Println(fmt.Sprintf("GzipEncode err:%v", err))
return
}
fmt.Println(fmt.Sprintf("res:%v", encode))
}
func TestGzipDecode(t *testing.T) {
decode, err := GzipDecode(context.Background(), "H4sIAAAAAAAA/8pIzcnJVyjPL8pJAQAAAP//AQAA//+FEUoNCwAAAA==")
if err != nil {
fmt.Println(fmt.Sprintf("GzipEncode err:%v", err))
return
}
fmt.Println(fmt.Sprintf("res:%v", decode))
}
当执行

当执行

测试结果符合预期。
此时可能会有一个疑问,一个
比如我们稍微将字符串变长一些,看看效果就知道压缩对于减少内存占用确实是有用的

3、为何压缩后还要用base64编码
我们可以将

可以看到压缩后字符串确实很小了,但是美观,且复制后对其解压会报错