当前位置: 首页 > 科技观察

下面说说Go是如何打包和压缩文件的

时间:2023-03-17 13:07:04 科技观察

文件打包、压缩和解压是经常用到的功能。我们可以使用tar、gzip等工具来完成这些操作。在Go中,标准库归档和压缩为我们提供了这些功能。通过本文的例子,你会发现在Go编程中生成和处理压缩包文件也是非常简单的。打包和压缩在开始写代码之前,我们需要先弄清楚打包和压缩的概念。打包,也称为存档,是指文件或目录的集合,这个集合存储在一个文件中。压缩是指利用算法对文件进行处理,以达到最大限度保留文件信息,使文件体积变小的目的。以打包工具tar为例,其生成的文件通常称为tar包,文件名通常以.tar结尾。然后使用其他压缩工具对tar包进行压缩,比如gzip压缩,得到一个压缩文件,通常以.tar.gz结尾(可以使用-z参数在tar中调用gzip)。tar包是文件的集合,它的结构也是由数据段组成的。每个数据段包括文件头(描述文件的元信息)和文件内容。+----------------------------------------+|标题||[名称][模式][所有者][组][大小]...|+--------------------------------------+|内容||XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX||------+|标题||[名称][模式][所有者][组][大小]...|+--------------------------------------------+|内容||XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX||----------------------+|...|archivelibrary打包和解包archivelibrary的中文意思是一个文件,它的作用是归档(package)和解包(unpacking)。它提供了两种解决方案:tar和zip,调用路径分别为archive/tar和archive/zip。我们以tar为例,展示如何打包和解包文件。首先新建一个目标包文件out.tar,然后构造一些文件数据readme.txt、gopher.txt和todo.txt进行归档。import("archive/tar"...)funcmain(){//创建一些文件并将其添加到存档中。tarPath:="out.tar"tarFile,err:=os.创建(tarPath)iferr!=nil{log.Fatal(err)}defertarFile.Close()tw:=tar.NewWriter(tarFile)defertw.Close()varfiles=[]struct{Name,Bodystring}{{"readme.txt","此存档包含一些文本文件。"},{"gopher.txt","Gophernames:\nGeorge\nGeoffrey\nGonzo"},{"todo.txt","Getanimalhandlinglicense."},}...}然后依次构造文件头信息,分别指定文件名、权限和大小(可以定义更多的文件头字段),然后通过tw依次调用WriteHeader和Write方法*tar.Writer类型的变量将打包的数据段(文件头+文件内容)写入到out.tar文件中。...for_,file:=rangefiles{hdr:=&tar.Header{Name:file.Name,Mode:0600,Size:int64(len(file.Body)),}iferr:=tw.WriteHeader(人类发展报告);err!=nil{log.Fatal(err)}如果_,err:=tw.Write([]byte(file.Body));err!=nil{log.Fatal(err)}}}execute上面的代码会得到打包好的out.tar文件,通过tar工具指定-tvf参数可以查看压缩包信息。$tar-tvfout.tar-rw------00038Jan11970readme.txt-rw------00035Jan11970gopher.txt-rw-------00028Jan11970todo.txt可以看出指定的文件信息(文件名、权限、大小)符合预期,但其他未指定的元信息有误,比如日期(直接给定默认值)。如果通过tar工具,我们可以执行以下命令来解压out.tar中的文件。$tar-xvfout.tarxreadme.txtgopher.txtxtodo.txt但是在程序中实现了,应该怎么实现呢?funcmain(){tarPath:="out.tar"tarFile,err:=os.Open(tarPath)iferr!=nil{log.Fatal(err)}defertarFile.Close()tr:=tar.NewReader(tarFile)for{hdr,err:=tr.Next()//存档结束iferr==io.EOF{break}iferr!=nil{log.Fatal(err)}fmt.Printf("Contentsof%s:",hdr.Name)if_,err:=io.Copy(os.Stdout,tr);err!=nil{log.Fatal(err)}fmt.Println()}}//Output:Contentsofreadme.txt:Thisarchivecontainssometextfiles.Contentsofgopher.txt:Gophernames:GeorgeGeoffreyGonzoTodo.txt的内容:获得动物处理许可证。首先需要打开out.tar,构造一个*tar.Reader类型的tr变量。之后使用tr.Next依次提取每个数据段的内容,使用io.Copy(os.Stdout,tr)将文件内容复制到标准输出。直到tr.Next遇到io.EOF,表示已经读取到归档文件的末尾,提取才会退出。Compress库压缩与解压compress库支持多种压缩方案,包括bzip2、flate、gzip、lzw和zlib,调用路径为compress/xxx。我们以常用的gzip为例,展示压缩和解压代码。如果上面的文件数据readme.txt、gopher.txt和todo.txt是一样的,我们想得到tar归档压缩出来的.tar.gz文件,应该怎么做呢?packagemainimport("archive/tar""compress/gzip"...)funcmain(){tarPath:="out.tar.gz"tarFile,err:=os.Create(tarPath)iferr!=nil{日志.Fatal(err)}defertarFile.Close()gz:=gzip.NewWriter(tarFile)defergz.Close()tw:=tar.NewWriter(gz)defertw.Close()...}非常简单!只需将tar.NewWriter(tarFile)更改为tar.NewWriter(gz),其中gz来自gzip.NewWriter(tarFile)。我们比较压缩和未压缩的存档tarball的大小,我们可以看到文件大小已从4.0K压缩到224B。$ls-alhout.tarout.tar.gz-rw-r--r--1名slp员工4.0K7月3日21:52out.tar-rw-r--r--1名slp员工224B7月3日21:53out.tar.gz同理,如果要对out.tar.gz文件进行解压解压,应该怎么做呢?packagemainimport("archive/tar""compress/gzip"...)funcmain(){tarPath:="out.tar.gz"tarFile,err:=os.Open(tarPath)iferr!=nil{日志.Fatal(err)}defertarFile.Close()gz,err:=gzip.NewReader(tarFile)iferr!=nil{log.Fatal(err)}defergz.Close()tr:=tar.NewReader(gz)...}还是很简单的!只需将tar.NewReader(tarFile)更改为tar.NewReader(gz),其中gz来自gzip.NewReader(tarFile)。总结本文展示了如何通过archive/tar包实现文件的打包和解包操作,以及如何通过compress/gzip包对tar包进行进一步的压缩和解压。在演示compress/gzip的使用时,可以多封装一层Writer/Reader,为tar归档文件增加压缩和解压功能。更好的是,如果你想切换打包/解包、压缩/解压策略,只需更换相应的Writer/Reader即可。而这种便利来自于Go出色的流式IO设计。当然,纸上谈兵取得的成绩是表面的,我知道这件事必须付诸实践。没有使用过archive和compress库的读者可以尝试使用本文中没有使用过的解决方案来尝试处理压缩文件。