在项目中需要使用到zip压缩与解压缩的功能,这类功能应该来说是很基础了,直接引用go官方的包archive/zip
,对于如何压缩文件夹的功能不是很熟悉,参考别人的代码加入到项目中,在实际使用中发现有些不符合预期的地方。
问题
代码引用自博客——Go压缩解压文件夹
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| func zipDir(dir, zipFile string) error {
fz, err := os.Create(zipFile) if err != nil { log.Debug("Create zip file failed: %s\n", err.Error()) return err } defer fz.Close()
w := zip.NewWriter(fz) defer w.Close()
filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if !info.IsDir() { fDest, err := w.Create(path[len(dir)+1:]) if err != nil { log.Printf("Create failed: %s\n", err.Error()) return err } fSrc, err := os.Open(path) if err != nil { log.Printf("Open failed: %s\n", err.Error()) return err } defer fSrc.Close() _, err = io.Copy(fDest, fSrc) if err != nil { log.Printf("Copy failed: %s\n", err.Error()) return err } } return nil })
return nil }
|
压缩文件夹函数确实能够压缩文件夹,但实际使用中,我发现和我需要的效果有些出入,例如,我的目录结果为:
1 2 3 4 5 6
| frist/ ├── second_1 │ └── 1.txt └── second_2 └── 1.txt 2 directories, 2 files
|
我需要连着frist
顶级目录一起进行压缩,但事与愿违,即使传入frist
的上一级目录,zipDir
函数依然只会压缩second_1
以及second_2
;而且,当我输出的目标ZIP文件是压缩的文件夹中时,压缩包内会出现一个名为zip
的临时文件。
解决
故此,我本着不应该没人做的更好的想法(嘿嘿~),找到了Golang: Working with ZIP archives。
可以压缩顶层目录,且压缩文件即使要输出到压缩目录,也不会出现什么异常。
Compressing
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
func zipit(source, target string) error { zipfile, err := os.Create(target) if err != nil { return err } defer zipfile.Close()
archive := zip.NewWriter(zipfile) defer archive.Close()
info, err := os.Stat(source) if err != nil { return nil }
var baseDir string if info.IsDir() { baseDir = filepath.Base(source) }
filepath.Walk(source, func(path string, info os.FileInfo, err error) error { if err != nil { return err }
header, err := zip.FileInfoHeader(info) if err != nil { return err }
if baseDir != "" { header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source)) }
if info.IsDir() { header.Name += "/" } else { header.Method = zip.Deflate }
writer, err := archive.CreateHeader(header) if err != nil { return err }
if info.IsDir() { return nil }
file, err := os.Open(path) if err != nil { return err } defer file.Close() _, err = io.Copy(writer, file) return err })
return err }
|
2019-3-22 11:47:46
发现一个小问题,当传入相对路径./testDir
给函数zipit
时,会出现两个顶层目录,是因为这行代码:
1
| header.Name = filepath.Join(baseDir, strings.TrimPrefix(path, source))
|
strings.TrimPrefix
,去掉前缀
1 2 3 4 5 6 7
| Code: var s = "¡¡¡Hello, Gophers!!!" s = strings.TrimPrefix(s, "¡¡¡Hello, ") s = strings.TrimPrefix(s, "¡¡¡Howdy, ") fmt.Print(s) Output: Gophers!!!
|
这行代码的意思也就是,去掉前缀的绝对路径,保留下要压缩文件的相对路径,但我们传入的是./testDir
,相对于路径来说,多了./
,所以其并不会去除成功,那么就会出现顶层目录出现两级的情况。
解决方法是:
- 传入相对路径时,不加
./
。
- 检查传入的路径是否为绝对路径。
1 2 3 4 5 6 7
| if isAbs := filepath.IsAbs(source); !isAbs { source = filepath.Base(source) }
|
加入以上代码可以解决问题,代码中,其实可以看到我屏蔽了——filepath.Abs
函数解决方式,这个函数调用了syscall
,也就是说借用了系统命令,这相对而言是比filepath.Base
更消耗资源的,因为涉及到了语言系统交互的层面,类似于go执行shell命令。
附上zip解压缩的代码,以便以后查看。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| func unzip(archive, target string) error { reader, err := zip.OpenReader(archive) if err != nil { return err }
if err := os.MkdirAll(target, 0755); err != nil { return err }
for _, file := range reader.File { path := filepath.Join(target, file.Name) if file.FileInfo().IsDir() { os.MkdirAll(path, file.Mode()) continue }
fileReader, err := file.Open() if err != nil { return err } defer fileReader.Close()
targetFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode()) if err != nil { return err } defer targetFile.Close()
if _, err := io.Copy(targetFile, fileReader); err != nil { return err } }
return nil }
|
这里我再放置一篇博主的:Golang: Working with Gzip and Tar
本文标题:压缩zip和解压缩unzip
文章作者:小师
发布时间:2019-03-20
最后更新:2022-05-04
原始链接:chunlife.top/2019/03/20/zip解压-压缩/
版权声明:本站所有文章均采用知识共享署名4.0国际许可协议进行许可