文章目录
- 一:简介
- 二: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编码
我们可以将
可以看到压缩后字符串确实很小了,但是美观,且复制后对其解压会报错