Go 关闭延迟中的gzip写入程序会导致数据丢失
我使用golangGo 关闭延迟中的gzip写入程序会导致数据丢失,go,Go,我使用golanggzip.NewWriter压缩一个片段,并使用延迟关闭()关闭编写器。但是从压缩数据读取时,它将返回意外的EOF。代码是: func main() { a := []byte{'a', 'b', 'c', 'd', 'e', 'f'} zippedData, err := zipData(a) if err != nil { panic(err) } unzippedData, err := unzipData(zi
gzip.NewWriter
压缩一个片段,并使用延迟关闭()
关闭编写器。但是从压缩数据读取时,它将返回意外的EOF
。代码是:
func main() {
a := []byte{'a', 'b', 'c', 'd', 'e', 'f'}
zippedData, err := zipData(a)
if err != nil {
panic(err)
}
unzippedData, err := unzipData(zippedData)
if err != nil {
panic(err)
}
fmt.Printf("%v\n", unzippedData)
}
zip函数是:
func zipData(originData []byte) ([]byte, error) {
var bf bytes.Buffer
gw := gzip.NewWriter(&bf)
defer gw.Close()
_, err := gw.Write(originData)
if err != nil {
return nil, errors.New(fmt.Sprintf("gzip data err: %v", err))
}
err = gw.Flush()
if err != nil {
return nil, err
}
// if I rm 'defer gw.Close()' and call 'gw.Close()' here, it'll be ok
logs.Debug("before gzip len: %v", len(originData))
logs.Debug("gzip len: %v", bf.Len())
return bf.Bytes(), nil
}
上面的zip函数使用defer gw.Close()
关闭gw
解压功能是:
func unzipData(zippedData []byte) ([]byte, error) {
dst := make([]byte, len(zippedData))
copy(dst, zippedData)
reader, err := gzip.NewReader(bytes.NewBuffer(dst))
if err != nil {
return nil, errors.New(fmt.Sprintf("unzip err :%v", err))
}
defer reader.Close()
data, err := ioutil.ReadAll(reader)
if err != nil {
return nil, errors.New(fmt.Sprintf("read err :%v", err))
}
return data, err
}
为什么
延迟gw.Close()
案例意外EOF
使用defer时,您缺少gzip页脚。根据关闭
文档:
Close通过将任何未写入的数据刷新到基础io.Writer并写入GZIP页脚来关闭写入程序。它不会关闭基础io.Writer
因此,即使Flush
刷新任何缓冲数据,它也不会写入页脚。使用延迟关闭,您将获得不包含页脚的字节数组并返回它,然后将页脚写入输出
返回前关闭编写器。使用“延迟”,您将丢失gzip页脚。根据
关闭
文档:
Close通过将任何未写入的数据刷新到基础io.Writer并写入GZIP页脚来关闭写入程序。它不会关闭基础io.Writer
因此,即使Flush
刷新任何缓冲数据,它也不会写入页脚。使用延迟关闭,您将获得不包含页脚的字节数组并返回它,然后将页脚写入输出
在返回之前关闭编写器。使用
defer
,gw.Close()
在return语句中调用bf.Bytes()
后运行。为了确保返回完整的内容,您应该在尝试从缓冲区读取字节之前显式调用gw.Close
最简单的代码修复方法是将
Flush
调用替换为Close
调用Flush
用于您尚未完成编写,但您已经完成压缩,因此调用Close
就足够了。使用defer
,gw.Close()
在返回语句中的bf.Bytes()
调用之后运行。为了确保返回完整的内容,您应该在尝试从缓冲区读取字节之前显式调用gw.Close
最简单的代码修复方法是将Flush
调用替换为Close
调用Flush
用于您尚未完成编写,但您已经完成压缩,因此调用Close
就足够了