悠悠楠杉
Golang如何做一个文件压缩解压工具:Golangzip包操作项目实践
本文通过实际项目示例,详细介绍如何使用 Golang 的 archive/zip 包实现文件的压缩与解压功能,涵盖目录遍历、文件写入 ZIP 归档、读取并提取压缩包内容等核心操作,帮助开发者快速构建轻量级跨平台压缩工具。
在日常开发中,我们经常需要处理文件归档和传输问题。无论是服务端日志打包、用户上传资源整理,还是配置文件分发,压缩与解压都是不可或缺的功能模块。Golang 以其简洁高效的特性,在构建这类工具时表现出色。借助标准库中的 archive/zip 包,我们可以轻松实现一个功能完整、性能可靠的 ZIP 压缩解压工具。
要开始这个项目,首先需要理解 Go 标准库对 ZIP 文件的支持机制。archive/zip 提供了读写 ZIP 格式文件的能力,无需引入第三方依赖,非常适合构建轻量级命令行工具或嵌入到服务中作为辅助功能。
我们先从压缩功能入手。目标是将指定目录下的所有文件(包括子目录)递归打包成一个 ZIP 文件。实现这一功能的关键在于遍历目录结构,并将每个文件以相对路径的形式写入 ZIP 归档。
go
package main
import (
"archive/zip"
"io"
"os"
"path/filepath"
)
func compress(src, dst string) error {
outFile, err := os.Create(dst)
if err != nil {
return err
}
defer outFile.Close()
zipWriter := zip.NewWriter(outFile)
defer zipWriter.Close()
return filepath.Walk(src, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
// 获取相对于源目录的路径
relPath, err := filepath.Rel(src, path)
if err != nil {
return err
}
// 创建 ZIP 中的文件头
header, err := zip.FileInfoHeader(info, "")
if err != nil {
return err
}
header.Name = filepath.ToSlash(relPath) // 使用正斜杠兼容跨平台
if info.IsDir() {
header.Name += "/" // 目录需以 / 结尾
} else {
header.Method = zip.Deflate // 使用 DEFLATE 压缩算法
}
writer, err := zipWriter.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 nil
})
}
上述代码中,我们使用 filepath.Walk 遍历源目录,为每个文件创建对应的 zip.FileHeader,并通过 zip.Writer.CreateHeader 写入数据。特别注意的是,目录路径必须以 / 结尾,且使用 filepath.ToSlash 确保路径分隔符统一为 /,这是 ZIP 格式的规范要求。
接下来实现解压功能。我们需要读取 ZIP 文件,逐个提取其中的条目,并还原到指定目录。
go
func decompress(zipPath, dest string) error {
reader, err := zip.OpenReader(zipPath)
if err != nil {
return err
}
defer reader.Close()
for _, file := range reader.File {
filePath := filepath.Join(dest, file.Name)
// 安全检查:防止路径穿越攻击
if !isValidRelPath(file.Name) {
continue
}
if file.FileInfo().IsDir() {
os.MkdirAll(filePath, os.ModePerm)
continue
}
// 确保父目录存在
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
return err
}
inFile, err := file.Open()
if err != nil {
return err
}
outFile, err := os.Create(filePath)
if err != nil {
inFile.Close()
return err
}
_, err = io.Copy(outFile, inFile)
outFile.Close()
inFile.Close()
if err != nil {
return err
}
// 保留原始文件权限(Unix系统)
os.Chmod(filePath, file.Mode())
}
return nil
}
// 简单路径校验,防止 ../ 路径穿越
func isValidRelPath(p string) bool {
if strings.Contains(p, "..") || strings.Contains(p, "\") {
return false
}
return true
}
在解压过程中,安全尤为重要。我们加入了路径合法性校验,避免恶意压缩包通过 ../ 实现目录穿越,造成文件覆盖风险。同时,通过 file.Mode() 恢复原始权限,提升工具的专业性。
最后,可以封装一个简单的 CLI 接口,接收命令行参数执行对应操作。例如:
go
func main() {
if len(os.Args) < 4 {
log.Fatal("用法: program [compress|decompress] src dest")
}
mode := os.Args[1]
src := os.Args[2]
dest := os.Args[3]
switch mode {
case "compress":
if err := compress(src, dest); err != nil {
log.Fatal(err)
}
case "decompress":
if err := decompress(src, dest); err != nil {
log.Fatal(err)
}
default:
log.Fatal("不支持的操作模式")
}
}
这样一个完整的、可运行的压缩解压工具就完成了。它不依赖外部库,具备良好的可移植性,适用于自动化脚本、服务端文件处理等多种场景。通过深入理解 archive/zip 的使用方式,我们不仅能完成基础功能,还能在此基础上扩展加密、分卷、进度反馈等高级特性。
