Golang实现文件压缩与解压功能
在实际业务开发中,文件压缩和解压是经常会用到的功能,比如日志归档、批量文件传输等场景。Golang标准库中已经提供了完善的压缩相关支持,我们可以利用archive/zip包快速实现文件的压缩与解压逻辑,无需依赖第三方库。本文将详细介绍两种功能的实现方式,并附上完整可运行的代码示例。
一、文件压缩功能实现
文件压缩的核心思路是:创建zip压缩文件,遍历需要压缩的源文件/目录,将每个文件的内容写入zip文件的对应条目中。需要注意处理目录的递归遍历,以及文件权限的保留。
下面是完整的文件压缩实现代码:
package main
import (
"archive/zip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
// Compress 压缩文件或目录到指定的zip文件
// srcPath 是要压缩的源文件或目录路径
// destZipPath 是生成的zip文件路径
func Compress(srcPath string, destZipPath string) error {
// 创建目标zip文件
zipFile, err := os.Create(destZipPath)
if err != nil {
return fmt.Errorf("创建zip文件失败: %v", err)
}
defer zipFile.Close()
// 创建zip写入器
zipWriter := zip.NewWriter(zipFile)
defer zipWriter.Close()
// 获取源路径的绝对路径,避免相对路径导致的条目路径错误
absSrcPath, err := filepath.Abs(srcPath)
if err != nil {
return fmt.Errorf("获取源路径绝对路径失败: %v", err)
}
// 获取源路径的根目录名,用于拼接zip内的条目路径
baseName := filepath.Base(absSrcPath)
// 定义遍历处理的回调函数
var walkFunc filepath.WalkFunc = func(path string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("遍历路径失败 %s: %v", path, err)
}
// 如果是目录,只创建目录条目(如果是根目录则跳过创建空条目)
if info.IsDir() {
// 根目录不需要单独创建条目
if path == absSrcPath {
return nil
}
// 计算zip内的条目路径
relPath, err := filepath.Rel(absSrcPath, path)
if err != nil {
return err
}
entryPath := filepath.Join(baseName, relPath) + "/"
// 创建目录条目
_, err = zipWriter.Create(entryPath)
return err
}
// 是文件,打开文件准备写入zip
file, err := os.Open(path)
if err != nil {
return fmt.Errorf("打开文件失败 %s: %v", path, err)
}
defer file.Close()
// 计算zip内的条目路径
relPath, err := filepath.Rel(absSrcPath, path)
if err != nil {
return err
}
entryPath := filepath.Join(baseName, relPath)
// 创建zip条目
writer, err := zipWriter.Create(entryPath)
if err != nil {
return fmt.Errorf("创建zip条目失败: %v", err)
}
// 拷贝文件内容到zip条目
_, err = io.Copy(writer, file)
return err
}
// 开始遍历源路径
err = filepath.Walk(absSrcPath, walkFunc)
if err != nil {
return fmt.Errorf("压缩过程出错: %v", err)
}
fmt.Printf("压缩完成,生成文件: %s\n", destZipPath)
return nil
}
func main() {
// 示例:压缩当前目录下的test_dir目录,生成test.zip
err := Compress("./test_dir", "./test.zip")
if err != nil {
fmt.Printf("压缩失败: %v\n", err)
}
}上述代码中,Compress函数接收源路径和目标zip路径两个参数,通过filepath.Walk递归遍历源路径下的所有文件和目录,为每个文件创建对应的zip条目并写入内容,目录则创建对应的目录条目。如果源路径是单个文件,同样可以正常处理,因为filepath.Walk对于单个文件只会遍历一次。
二、文件解压功能实现
文件解压的核心思路是:打开zip文件,遍历其中的所有条目,根据条目类型(文件/目录)创建对应的本地文件或目录,并将文件条目的内容写入本地文件。
下面是完整的解压实现代码:
package main
import (
"archive/zip"
"fmt"
"io"
"os"
"path/filepath"
)
// Decompress 解压zip文件到指定目录
// zipPath 是要解压的zip文件路径
// destDir 是解压目标目录路径
func Decompress(zipPath string, destDir string) error {
// 打开zip文件
zipReader, err := zip.OpenReader(zipPath)
if err != nil {
return fmt.Errorf("打开zip文件失败: %v", err)
}
defer zipReader.Close()
// 遍历zip中的所有条目
for _, file := range zipReader.File {
// 拼接目标路径,避免路径穿越攻击
// 例如zip条目路径为../../etc/passwd的情况,需要过滤
targetPath := filepath.Join(destDir, file.Name)
// 再次校验目标路径是否在destDir下
absDestDir, err := filepath.Abs(destDir)
if err != nil {
return err
}
absTargetPath, err := filepath.Abs(targetPath)
if err != nil {
return err
}
if !strings.HasPrefix(absTargetPath, absDestDir) {
return fmt.Errorf("非法的zip条目路径: %s", file.Name)
}
// 如果是目录,创建目录
if file.FileInfo().IsDir() {
err := os.MkdirAll(targetPath, os.ModePerm)
if err != nil {
return fmt.Errorf("创建目录失败 %s: %v", targetPath, err)
}
continue
}
// 是文件,先确保父目录存在
if err := os.MkdirAll(filepath.Dir(targetPath), os.ModePerm); err != nil {
return fmt.Errorf("创建父目录失败: %v", err)
}
// 创建目标文件
outFile, err := os.OpenFile(targetPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, file.Mode())
if err != nil {
return fmt.Errorf("创建目标文件失败 %s: %v", targetPath, err)
}
// 打开zip中的文件条目
rc, err := file.Open()
if err != nil {
outFile.Close()
return fmt.Errorf("打开zip条目失败: %v", err)
}
// 拷贝内容到目标文件
_, err = io.Copy(outFile, rc)
// 先关闭所有打开的资源,再处理错误
rc.Close()
outFile.Close()
if err != nil {
return fmt.Errorf("写入文件内容失败 %s: %v", targetPath, err)
}
}
fmt.Printf("解压完成,文件位于: %s\n", destDir)
return nil
}
func main() {
// 示例:解压当前目录下的test.zip到./unzip_dir目录
err := Decompress("./test.zip", "./unzip_dir")
if err != nil {
fmt.Printf("解压失败: %v\n", err)
}
}解压函数中特别加入了路径合法性校验,避免zip条目路径包含../等字符导致的路径穿越攻击,这是实际生产环境中必须考虑的安全点。同时,对于文件条目会先创建其父目录,避免因为父目录不存在导致文件创建失败。
三、功能测试与注意事项
测试时可以按照以下步骤验证功能:
- 先创建测试目录
test_dir,在其中放入几个测试文件和子目录,运行压缩程序生成test.zip - 删除原
test_dir目录,运行解压程序将test.zip解压到unzip_dir,对比解压后的文件和原文件是否一致
需要注意的几点:
- 压缩时如果源路径是绝对路径,zip内的条目路径会保留完整的目录结构,建议根据需求调整条目路径的拼接逻辑
- 大文件压缩解压时可以添加进度回调,提升用户体验
- 如果需要支持其他压缩格式(如tar.gz),可以使用
compress/gzip和archive/tar包,实现思路类似
Golang文件压缩文件解压archive_zipGo语言教程 本作品最后修改时间:2026-05-23 10:46:11