// 解压缩数据gzr, err := gzip. NewReader(buffer) iferr != nil{ fmt. Println(err) return}fmt. Print( "解压缩后的内容:") // 读取解压缩的内容io. Copy(os. Stdout, gzr) // 关闭Reader对象// 与其关联的基础数据流不会关闭gzr. Close
创建Writer实例后,只要调用Write方法就可以写入要压缩的数据;反之,调用Reader实例的Read方法可以读取解压缩后的数据。若到了数据流末尾,就会返回EOF错误。上述示例中,调用io.Copy函数把解压缩出来的数据读出,并写入到标准输出流(os.Stdout)。
在写完(或读完)数据后,都可以调用Close方法关闭Writer(或Reader)对象,但不会关闭与之关联的基础数据流。对于压缩操作而言,调用Writer对象的Close方法后数据才会真正写入基础流中。如果忘记调用Close方法,有可能导致压缩后的数据不完整(未完全写入基础流)。
再看一个示例,此例将使用Gzip算法压缩音频文件。
gzw := gzip.NewWriter(outFile)// 设置在压缩包内显示的文件名gzw.Name = "music.mp3"// 压缩并写入数据io.Copy(gzw, inFile)// 关闭Writer对象并把数据写入文件gzw.Close// 关闭文件inFile.CloseoutFile.Close
在压缩文件时,可以设置Writer对象的Name属性(来自Header类型),以指定文件在gzip文档中显示的名称。
压缩多个文件
Writer对象支持压缩多个文件(或多段数据流)并写入基础数据流,其核心是调用Reset方法——每写完一个文件,先调用Close方法把数据写入基础流,然后调用Reset方法重置Writer对象的状态,这样就可以在不需要重新创建新Writer实例的情况下继续写入文件数据。
下面代码将向Gzip文档添加三个文件。
Gzip算法在写入压缩数据时,会将所有文件(或数据流)的内容连接起来,而不是单独存储,也就是把多个数据流合并为一个流来处理。所以,在解压数据时,如果使用的是Reader的默认行为,那么它会把Gzip文档中所有的文件内容一次性读出,就像下面这样:
读出来的结果如下:
文件内容:示例文本 1示例文本 2示例文本 3
从上述结果可以看到,Reader对象只读了一次,当调用Reset方法返回EOF(文件末尾),for循环退出,也就是说,三个文件的内容被一次性全读出来。如果希望把文件逐个读出,则需要关闭Reader对象的默认行为,详情可参考如下内容。
解压多个文件
Reader类型有一个名为Multistream的方法,它接收一个bool类型的参数值。若该参数的值为false,则表示禁用Reader对象的默认行为,就可以逐个读取Gzip文档中的文件。
下面示例将使用Gzip算法压缩五个文件,在解压时将它们逐个读出来。
// 解压gzr, err := gzip.NewReader(buffer)iferr != nil{ fmt.Println(err)return}// 逐一读出文件for{ // 此处调用是关键gzr.Multistream( false) fmt.Printf( "文件:%s\n", gzr.Name) fmt.Print( "文件内容:") // 读出文件内容,并写入标准输出流io.Copy(os.Stdout, gzr)fmt.Print( "\n") // 重置,读取下一个文件err := gzr.Reset(buffer)iferr == io.EOF { // 到达文档末尾,退出循环break}}// 关闭Reader对象gzr.Close
在解压Gzip文档时要注意,每一次读取文件之前都必须调用一次Multistream方法,并向参数传递false值。这是因为for循环末尾会调用Reset方法来重置Reader对象,这会使Reader对象恢复为默认行为。
示例依次解压五个文件,并输出文件名和文件内容。
文件: file-02.txt文件内容:示例文本 --2
文件: file-03.txt文件内容:示例文本 --3
文件: file-04.txt文件内容:示例文本 --4