Go语言zip归档文件的读写操作
来源:云海天教程
时间:2023-01-07 11:59:51 428浏览 收藏
本篇文章向大家介绍《Go语言zip归档文件的读写操作》,主要包括文件处理,具有一定的参考价值,需要的朋友可以参考一下。
Go语言的标准库提供了对几种压缩格式的支持,其中包括 gzip,因此 Go 程序可以无缝地读写 .gz 扩展名的 gzip 压缩文件或非 .gz 扩展名的非压缩文件。此外,标准库也提供了读和 写 .zip 文件、tar 包文件 (.tar 和 .tar.gz ),以及读 .bz2 文件(即 .tar .bz2 文件)的功能。本节我们将主要介绍 zip 归档文件的读写操作。
创建 zip 归档文件
要使用 Zip 包来压缩文件,我们首先必须打开一个用于写的文件,然后创建一个 *zip.Writer 值来往其中写入数据。然后,对于每一个我们希望加入 .zip 归档文件的文件,我们必须读取该文件并将其内容写入 *zip.Writer 中。该 pack 程序使用了 createZip() 和 writeFileToZip() 两个函数以这种方式来创建一个 .zip 文件。func createZip(filename string, files []string) error { file, err := os.Create(filename) if err != nil { return err } defer file.Close() zipper := zip.NewWriter(file) defer zipper.Close() for _, name := range files { if err := writeFileToZip(zipper, name); err != nil { return err } } return nil}该 createZip() 函数和 writeFileToZip() 函数都比较简短,因此容易让人觉得应该写入一个函数中。这是不明智的,因为在该 for 循环中我们可能打开一个又一个的文件(即 files 切片中的所有文件),从而可能超出操作系统允许的文件打开数上限。这点我们在前面章节中己有简短的阐述。
当然,我们可以在每次迭代中调用。s.File.Close(),而非延迟执行它,但这样做还必须保证程序无论是否出错文件都必须关闭。因此,最为简便而干净的解决方案是,像这里所做的那样,总是创建一个独立的函数来处理每个独立的文件。
func writeFileToZip(zipper *zip.Writer, filename string) error { file, err := os.Open(filename) if err != nil { return err } defer file.Close() info, err := file.Stat() if err != nil { return err } header, err := zip.FilelnfoHeader(info) if err != nil { return err } header.name = sanitizedName(filename) writer, err := zipper.CreateHeader(header) if err != nil { return err } _, err = io.Copy(writer, file) return err}首先我们打开需要归档的文件以供读取,然后延迟关闭它。这是我们处理文件的老套路了。
接下来,我们调用 os.File.Stat() 方法来取得包含时间戳和权限标志的 os.Fileinfo 值。然后,我们将该值传给 zip.FileInfoHeader() 函数,该函数返回一个 zip.FileHeader 值,其中保存了时间戳、权限以及文件名。在压缩文件中,我们无需使用与原始文件名一样的文件名,因此这里我们使用净化过的文件名来覆盖原始文件名(保存在 zip.FileHeader.Name 字段中)。
头部设置好之后,我们将其作为参数调用 zip.CreateHeader() 函数。这会在 .zip 压缩文件中创建一个项,其中包含头部的时间戳、权限以及文件名,并返回一个 io.Writer,我们可以往其中写入需要被压缩的文件的内容。
为此,我们使用了 io.Copy() 函数,它能够返回所复制的字节数,以及一个为空或者非空的错误值。
如果在任何时候发生错误,该函数就会立即返回并由调用者处理错误。如果最终没有错误发生,那么该 .zip 压缩文件就会包含该给定文件。
func sanitizedName(filename string) string{ if len(filename) > 1 && filename[1] == ':' && runtime.GOOS == *'windows" { filename = filename[2:] } filename = filepath.ToSlash(filename) filename = strings. TrimLef t( filename, "/.**) return strings.Replace(filename, ".・/", -1)}如果一个归档文件中包含的文件带有绝对路径或者含有“..”路径组件,我们就有可能在解开归档的时候意外覆盖本地重要文件。为了降低这种风险,我们对保存在归档文件里每个文件的文件名都做了相应的净化。
该 sanitizedName() 函数会删除路径头部的盘符以及冒号(如果有的话),然后删除头部任何目录分隔符、点号以及任何路径组件,并将文件分隔符强制转换成正向斜线。
解开 zip 归档文件
解开一个 .zip 归档文件与创建一个归档文件一样简单,只是如果归档文件中包含带有路径的文件名,就必须重建目录结构。func unpackZip(filename string) error { reader, err := zip.OpenReader(filename) if err != nil { return err } defer reader.Close() for _, zipFile := range reader.Reader.File { name := san it izedName (zipFiJLe.Name) mode := zipFile.Mode() if mode.IsDir() { if err = os.MkdirAl1(name, 0755); err != nil { return err } } else { if err = unpackZippedFile(name, zipFile); err != nil { return err } } } return nil}该函数打开给定的 .zip 文件用于读取。这里没有使用 os.Open() 函数来打开文件后调用 zip.NewReader(),而是使用 zip 包提供的 zip.OpenReader() 函数,它可以方便地打开并返回一个 *zip.ReadCloser 值让我们使用。
zip.ReadCloser 最为重要的一点是它包含了导出的 zip.Reader 结构体字段,其中包含一个包含指向 zip.File 结构体指针的 []*zip.File 切片,其中的每一项表示 .zip 压缩文件中的一个文件。
我们迭代访问该 reader 的 zip.File 结构体,并创建一个净化过的文件及目录名(使用我们在 pack 程序中用到的 sanitizedName() 函数),以降低覆盖重要文件的风险。
如果遇到一个目录(由 *zip.File 的 os.FileMode 的 IsDir() 方法报告),我们就创建一个目录。os.MkdirAll() 函数传入了有用的属性信息,会自动创建必要的中间目录以创建特定的目标目录,如果目录已经存在则会安全地返回 nil 而不执行任何操作。如果遇到的是一个文件,则交由自定义的 unpackZippedFile() 函数进行解压。
func unpackZippedFile(filename string, zipFile *zipFile) error { writer, err := os.Create(filename) if err != nil { return err } defer writer.Close() reader, err := zipFile.Open() if err != nil { return err } defer reader.Close() if _, err = io.Copy(writer, reader); err != nil { return err } if filename == zipFile.Name { fm.Println(filename) } else { fmt.Printf("%s [%s]", filename, zipFile.Name) } return nil}unpackZippedFile() 函数的作用就是将 .zip 归档文件里的单个文件抽取出来,写到 filename 指定的文件里去。首先它创建所需要的文件,然后,使用 zip.File.Open() 函数打开指定的归档文件,并将数据复制到新创建的文件里去。
最后,如果没有错误发生,该函数会往终端打印所创建文件的文件名,如果处理后的文件名与原始文件名不一样,则将原始文件名包含在方括号中。
值得注意的是,该 *zip.File 类型也有一些其他的方法,如 zip.File.Mode()(在前面的 unpackZip() 函数中已有使用),zip.File.ModTime()(以 time.Time 值返回文件的修改时间)以及返回文件的 os.Fileinfo 值的 zip.Fileinfo()。
以上就是本文的全部内容了,是否有顺利帮助你解决问题?若是能给你带来学习上的帮助,请大家多多支持golang学习网!更多关于Golang的相关知识,也可关注golang学习网公众号。
-
225 收藏
-
389 收藏
-
250 收藏
-
137 收藏
-
190 收藏
-
113 收藏
-
140 收藏
-
277 收藏
-
186 收藏
-
346 收藏
-
286 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 542次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 507次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 497次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 484次学习