golang zip压缩/解压缩用法

最近有个需求,需要写个脚本,但要编译为exe可执行文件,首先考虑python打包,奈何使用pyinstaller打包后,出现各种各样的运行错误,最后放弃了,改为golang重写。因为要用到创建和解压zip文件,golang中使用zip模块的功能,远没有python那么方便。

一、压缩部分

把文件或者文件夹压缩为zip文件,主要过程就是创建目的zip文件,然后遍历源目录,将源目录下的文件拷贝到目的zip文件中,最重要的2个方法:

1、zip.newWriter  创建一个向zip文件中写入的writer

2、writer.CreateHeader 向zip文件头中写入内容

其过程网上一搜一大把,雷同的很多,果然大家都是代码的搬运工。在网上看到用的最多的一例,见原文连接:https://blog.csdn.net/lengyuezuixue/article/details/79651549,

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"strings"
)

//压缩文件
//files 文件数组,可以是不同dir下的文件或者文件夹
//dest 压缩文件存放地址
func Compress(files []*os.File, dest string) error {
	d, _ := os.Create(dest)
	defer d.Close()
	w := zip.NewWriter(d)
	defer w.Close()
	for _, file := range files {
		err := compress(file, "", w)
		if err != nil {
			return err
		}
	}
	return nil
}

func compress(file *os.File, prefix string, zw *zip.Writer) error {
	info, err := file.Stat()
	if err != nil {
		return err
	}
	if info.IsDir() {
		prefix = prefix + "/" + info.Name()
		fileInfos, err := file.Readdir(-1)
		if err != nil {
			return err
		}
		for _, fi := range fileInfos {
			f, err := os.Open(file.Name() + "/" + fi.Name())
			if err != nil {
				return err
			}
			err = compress(f, prefix, zw)
			if err != nil {
				return err
			}
		}
	} else {
		header, err := zip.FileInfoHeader(info)
		header.Name = prefix + "/" + header.Name
		if err != nil {
			return err
		}
		writer, err := zw.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, file)
		file.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

以上代码Compress方法在大多数情况下没有问题,但有一个缺陷,不能压缩空目录,源目录中含有空目录及嵌套的空目录,都不能被压缩。仔细读一遍代码可知,以上代码是通过递归遍历源目录中的文件,然后将文件copy到目的zip中,第57行。但没有考虑到空目录的情况,从第31行,IsDir后,对空目录没有做任何处理导致空目录被跳过。实例运行如图:

可以看到对空目录,以上代码没有进行处理。像将其优化,增加对非空目录的处理,完整如下:

import (
	"archive/zip"
	"fmt"
	"io"
	"os"
	"strings"
)

//压缩文件
//src 可以是不同dir下的文件或者文件夹
//dest 压缩文件存放地址
func Compress(src string, dest string) error {
	f, err := os.Open(src)
	if err != nil {
		return err
	}
	files := []*os.File{f}
	d, _ := os.Create(dest)
	defer d.Close()
	w := zip.NewWriter(d)
	defer w.Close()
	for _, file := range files {
		err := compress(file, "", w)
		if err != nil {
			return err
		}
	}
	return nil
}

func compress(file *os.File, prefix string, zw *zip.Writer) error {
	info, err := file.Stat()
	if err != nil {
		return err
	}
	if info.IsDir() {
		prefix = prefix + "/" + info.Name()
		fileInfos, err := file.Readdir(-1)
		if err != nil {
			return err
		}
		// 增加对空目录的判断
		if len(fileInfos) <= 0 {
			header, err := zip.FileInfoHeader(info)
			header.Name = prefix
			if err != nil {
				fmt.Println("error is:"+err.Error())
				return err
			}
			_, err = zw.CreateHeader(header)
			if err != nil {
				fmt.Println("create error is:"+err.Error())
				return err
			}
			file.Close()
		}
		for _, fi := range fileInfos {
			f, err := os.Open(file.Name() + "/" + fi.Name())
			if err != nil {
				return err
			}
			err = compress(f, prefix, zw)
			if err != nil {
				return err
			}
		}
	} else {
		header, err := zip.FileInfoHeader(info)
		header.Name = prefix + "/" + header.Name
		if err != nil {
			return err
		}
		writer, err := zw.CreateHeader(header)
		if err != nil {
			return err
		}
		_, err = io.Copy(writer, file)
		file.Close()
		if err != nil {
			return err
		}
	}
	return nil
}

//解压
func DeCompress(zipFile, dest string) error {
	reader, err := zip.OpenReader(zipFile)
	if err != nil {
		return err
	}
	defer reader.Close()
	for _, file := range reader.File {
		rc, err := file.Open()
		if err != nil {
			return err
		}
		defer rc.Close()
		filename := dest + file.Name
		err = os.MkdirAll(getDir(filename), 0755)
		if err != nil {
			return err
		}
		w, err := os.Create(filename)
		if err != nil {
			return err
		}
		defer w.Close()
		_, err = io.Copy(w, rc)
		if err != nil {
			return err
		}
		w.Close()
		rc.Close()
	}
	return nil
}

func getDir(path string) string {
	return subString(path, 0, strings.LastIndex(path, "/"))
}

func subString(str string, start, end int) string {
	rs := []rune(str)
	length := len(rs)

	if start < 0 || start > length {
		panic("start is wrong")
	}

	if end < start || end > length {
		panic("end is wrong")
	}

	return string(rs[start:end])
}

在43行-56行增加了对空目录的处理,直接将目录名写入到zip header中。可以完美解决空目录不处理的问题。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Golang中,可以使用`archive/zip`包来进行zip压缩。下面是一个简单的示例代码,演示如何使用该包进行文件或目录的压缩: ```go package main import ( "archive/zip" "fmt" "io" "os" "path/filepath" ) func main() { // 要压缩的文件或目录路径 source := "/path/to/source" // 压缩后的zip文件路径 destination := "/path/to/destination.zip" err := zipFiles(destination, source) if err != nil { fmt.Println(err) } else { fmt.Println("压缩完成!") } } func zipFiles(destination string, source string) error { // 创建一个新的zip文件 zipFile, err := os.Create(destination) if err != nil { return err } defer zipFile.Close() // 创建zip.Writer,用于向zip文件中写入数据 archive := zip.NewWriter(zipFile) defer archive.Close() // 递归遍历源路径下的所有文件和目录 filepath.Walk(source, func(path string, info os.FileInfo, err error) error { if err != nil { return err } // 获取源路径下的相对路径,用于在zip文件中创建相应的目录结构 relativePath, err := filepath.Rel(source, path) if err != nil { return err } // 创建一个新的文件或目录头部 header, err := zip.FileInfoHeader(info) if err != nil { return err } // 设置头部的名称为相对路径 header.Name = relativePath if info.IsDir() { // 如果是目录,则写入一个目录头部到zip文件中 header.Name += "/" header.Method = zip.Store } else { // 如果是文件,则写入文件内容到zip文件中 header.Method = zip.Deflate } // 创建一个新的文件或目录条目,写入zip.Writer中 writer, err := archive.CreateHeader(header) if err != nil { return err } if !info.IsDir() { // 如果是文件,则打开源文件并将内容复制到zip文件中 file, err := os.Open(path) if err != nil { return err } defer file.Close() _, err = io.Copy(writer, file) if err != nil { return err } } return nil }) return nil } ``` 你可以将`source`和`destination`变量替换为你希望的路径,然后运行代码即可实现压缩。请确保你拥有对源文件或目录的读取权限,并且目标路径是一个有效的zip文件路径。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值