golang压缩和解压缩zip文件的一些总结

golang压缩和解压缩zip文件

一个简单例子说明如何使用golang提供的archive/zip包实现zip文件的压缩和解压缩。

压缩

提供两个参数:

  1. 一个是输入源文件
  2. 另一个是输出zip压缩文件

其中输入源文件可以是一个文件,也可以好似一个目录,如果是目录那么目录下的所有文件都会被压缩到输出。

当然这个函数很容易被扩展成支持多个输入源文件/目录的情况。

// srcFile could be a single file or a directory
func Zip(srcFile string, destZip string) error {
    zipfile, err := os.Create(destZip)
    if err != nil {
        return err
    }
    defer zipfile.Close()

    archive := zip.NewWriter(zipfile)
    defer archive.Close()

    filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }

        header, err := zip.FileInfoHeader(info)
        if err != nil {
            return err
        }


        header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile) + "/")
        // header.Name = path
        if info.IsDir() {
            header.Name += "/"
        } else {
            header.Method = zip.Deflate
        }

        writer, err := archive.CreateHeader(header)
        if err != nil {
            return err
        }

        if ! info.IsDir() {
            file, err := os.Open(path)
            if err != nil {
                return err
            }
            defer file.Close()
            _, err = io.Copy(writer, file)
        }
        return err
    })

    return err
}

解压缩

也有提供两个参数:

  1. 一个是输入源zip文件
  2. 另一个是输出路径

func Unzip(zipFile string, destDir string) error {
    zipReader, err := zip.OpenReader(zipFile)
    if err != nil {
        return err
    }
    defer zipReader.Close()

    for _, f := range zipReader.File {
        fpath := filepath.Join(destDir, f.Name)
        if f.FileInfo().IsDir() {
            os.MkdirAll(fpath, os.ModePerm)
        } else {
            if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
                return err
            }

            inFile, err := f.Open()
            if err != nil {
                return err
            }
            defer inFile.Close()

            outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
            if err != nil {
                return err
            }
            defer outFile.Close()

            _, err = io.Copy(outFile, inFile)
            if err != nil {
                return err
            }
        }
    }
    return nil
}

问题来了

  1. 使用我们第一个Zip函数压缩的文件,如果传入的是文件而不是文件夹,例如srcFile是 D:\test\1.txt。这样用Unzip函数无法解压,比如destDir路径设置为D:\UnzipFile,执行
    fpath := filepath.Join(destDir, f.Name)后 fpath为 D:\UnzipFile\D:\test\1.txt
  2. header.Name += "/" 这句代码直接使用  /  不太好

解决办法

1.修改zip函数,如果传入的是文件夹,保存的路径层级去掉文件夹路径。如果传入的是文件,修改Unzip函数处理 f.name  去掉盘符冒号反斜杠 ,如 D:\

2. 将 “/”  替换为 

 string(os.PathSeparator)

修改后的代码

压缩文件:

// srcFile could be a single file or a directory
func Zip(srcFile string, destZip string) error {
	zipfile, err := os.Create(destZip)
	if err != nil {
		return err
	}
	defer zipfile.Close()

	archive := zip.NewWriter(zipfile)
	defer archive.Close()

	filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
		if err != nil {
			return err
		}
		// 如果是源路径(文件夹),提前进行下一个遍历
		if path == srcFile {
			if info.IsDir() {
				return nil
			}
		}

		header, err := zip.FileInfoHeader(info)
		if err != nil {
			return err
		}

		//header.Name = path
		//header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile) + "/")
		header.Name = strings.TrimPrefix(path, srcFile + string(os.PathSeparator))
		if info.IsDir() {
			header.Name += string(os.PathSeparator)
		} else {
			header.Method = zip.Deflate
		}
		writer, err := archive.CreateHeader(header)
		if err != nil {
			return err
		}

		if ! info.IsDir() {
			file, err := os.Open(path)
			if err != nil {
				return err
			}
			defer file.Close()
			_, err = io.Copy(writer, file)
		}
		return err
	})

	return err
}

解压文件:

func Unzip(zipFile string, destDir string) error {
	zipReader, err := zip.OpenReader(zipFile)
	if err != nil {
		return err
	}
	defer zipReader.Close()

	for _, f := range zipReader.File {
		fpath := ""
		name := f.Name
		if find := strings.Contains(name,":"); find {
			name = name[strings.Index(name,":") + 2 : len(name)  ]
			fpath = filepath.Join(destDir, name)
		}else{
			fpath = filepath.Join(destDir, name)
		}

		if f.FileInfo().IsDir() {
			os.MkdirAll(fpath, os.ModePerm)
		} else {
			if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
				return err
			}

			inFile, err := f.Open()
			if err != nil {
				return err
			}
			defer inFile.Close()

			outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
			if err != nil {
				return err
			}
			defer outFile.Close()

			_, err = io.Copy(outFile, inFile)
			if err != nil {
				return err
			}
		}
	}
	return nil
}

其他中文编码问题:

Go语言zip解压._JieLinDee的专栏-CSDN博客package mainimport ("archive/zip""fmt""io""os")func main() {r, err := zip.OpenReader(`server_update_20151020.zip`)if err != nil {fmt.Println(err)return}for _, k := range r.Rhttps://blog.csdn.net/fyxichen/article/details/49273883

  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值