猿问

在 defer 中关闭 gzip writer 会导致数据丢失

我使用 golanggzip.NewWriter压缩切片,并defer Close()关闭 writer。但是当从压缩数据中读取时,它会返回unexpected 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)


}



压缩功能是:


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

}


为什么要defer gw.Close()案例unexpected EOF?


素胚勾勒不出你
浏览 168回答 2
2回答

一只名叫tom的猫

使用 defer,您会丢失 gzip 页脚。根据Close文档:Close 通过将任何未写入的数据刷新到底层 io.Writer 并写入 GZIP 页脚来关闭 Writer。它不会关闭底层的 io.Writer。因此,即使Flush刷新任何缓冲数据,它也不会写入页脚。通过延迟关闭,您将获得不包含页脚的字节数组并将其返回,然后将页脚写入输出。在返回之前关闭 writer。

慕姐4208626

With defer,在 return 语句中gw.Close()的调用之后运行。bf.Bytes()为了确保返回完整的内容,您应该gw.Close在尝试从缓冲区读取字节之前显式调用。最简单的代码修复方法是将Flush调用替换为Close调用。Flush是在你没有写完的时候,但在你的情况下你已经完成了压缩,所以调用Close应该就足够了。
随时随地看视频慕课网APP

相关分类

Go
我要回答