当前位置:首页 > 文章列表 > Golang > Go教程 > Go tar 怎样打包目录并保留模式

Go tar 怎样打包目录并保留模式

来源:17golang原创 2026-10-22 00:34:53 0浏览 收藏

我在做备份导出时,最容易踩的坑不是把文件写进 tar,而是归档里到底保存了什么:绝对路径会污染部署环境,直接拼路径会漏掉目录模式,符号链接又不能当普通文件读取。比较稳的做法是用 filepath.WalkDir 遍历,用 tar.FileInfoHeader 生成头部,再把路径改成相对路径;普通文件复制内容,目录只写头部,符号链接保存目标。

官方地址:https://pkg.go.dev/archive/tar

要点速览
  • FileInfoHeader 会把文件类型、大小和模式带入 tar 头部,但归档名称要自己改成相对路径。
  • 目录不需要 io.Copy;符号链接应通过 Linkname 保存目标,不能跟随链接读取内容。
  • tar 头部保留的是模式信息,解包后的最终权限仍可能受到 umask、解包工具和目标系统规则影响。

先把模式和归档路径分开处理

archive/tar 的 Writer 是顺序写入器。每个条目先调用 WriteHeader,之后才写该条目的内容。FileInfoHeader 可以从 fs.FileInfo 生成部分头部,其中包括模式、大小和文件类型;但 FileInfo.Name() 只有基本名,所以遍历目录后必须把 Header.Name 改成相对路径。

这一区分很重要:模式来自源文件的 FileMode,路径来自归档策略。不要把源目录的绝对路径直接放入 Header.Name,也不要用字符串替换猜测目录层级。

用 WalkDir 写一个保留模式的打包函数

package main

import (
	"archive/tar"
	"io"
	"os"
	"path/filepath"
)

// archiveDir 把 src 的内容写入 dst;归档内路径统一从 src 根目录计算。
func archiveDir(src, dst string) (err error) {
	out, err := os.Create(dst)
	if err != nil {
		return err
	}
	defer func() {
		// 让调用方同时拿到遍历错误和关闭输出文件的错误。
		if closeErr := out.Close(); err == nil {
			err = closeErr
		}
	}()

	tw := tar.NewWriter(out)
	defer func() {
		// Close 会写出 tar 的结束块,不能只关闭底层文件。
		if closeErr := tw.Close(); err == nil {
			err = closeErr
		}
	}()

	return filepath.WalkDir(src, func(path string, d os.DirEntry, walkErr error) error {
		if walkErr != nil {
			return walkErr // 访问失败时停止,避免生成不完整归档。
		}

		info, err := d.Info()
		if err != nil {
			return err
		}
		rel, err := filepath.Rel(src, path)
		if err != nil {
			return err
		}
		if rel == "." {
			rel = filepath.Base(src) // 保留根目录条目,便于解包后还原层级。
		}

		link := ""
		if d.Type()&os.ModeSymlink != 0 {
			link, err = os.Readlink(path) // 只保存链接目标,不跟随链接。
			if err != nil {
				return err
			}
		}
		hdr, err := tar.FileInfoHeader(info, link)
		if err != nil {
			return err
		}
		hdr.Name = filepath.ToSlash(rel) // tar 路径使用正斜杠,避免跨平台差异。
		if err := tw.WriteHeader(hdr); err != nil {
			return err
		}

		if !info.Mode().IsRegular() {
			return nil // 目录、链接等只需头部;不要把它们当普通文件读取。
		}
		file, err := os.Open(path)
		if err != nil {
			return err
		}
		defer file.Close() // 当前回调结束后释放文件描述符。
		_, err = io.Copy(tw, file)
		return err
	})
}
Go WalkDir 到 tar.Writer 的目录打包静态结构说明图
图1:静态说明图展示 WalkDir、相对路径、FileInfoHeader 与 tar.Writer 的职责边界,不是运行截图。

这段实现里,FileInfoHeader 负责把 info.Mode() 反映到 Header.Mode,所以普通文件和目录的权限位会进入 tar 头部。Header.Name 则由 Rel 计算,最后用 ToSlash 统一成归档格式。根目录是否保留是策略问题;上例保留它,解包后更容易得到明确的顶层目录。

三种文件类型对应三种写法

类型头部字段正文处理
普通文件Mode、Size写头后用 io.Copy 复制内容
目录ModeDir、名称末尾的 /只写头,不复制正文
符号链接ModeSymlink、Linkname只保存链接目标,不读取目标文件

目录和符号链接都不能按普通文件调用 io.Copy。另外,WalkDir 默认不跟随符号链接,这正好避免把链接指向的目录递归打进包里。若业务明确需要特殊文件、硬链接或扩展属性,应单独设计归档策略,不能假设 FileInfoHeader 自动覆盖所有系统语义。

Go tar 普通文件目录符号链接与模式字段关系说明图
图2:结构图展示不同文件类型对应的 tar Header 字段;它是原创说明图,不代表实际归档结果。

模式保留的边界和收尾检查

“保留模式”指 tar 头部记录了源条目的模式,不等于任何解包环境都会得到完全相同的最终权限。解包程序可能受 umask 影响,Windows 等系统也未必按 Unix 权限语义还原。因此发布前应明确目标平台,并在测试包中检查条目名称、类型、模式和链接目标。

还要特别注意两个边界:第一,输出文件不要放在正在遍历的 src 目录里,否则它可能被自己再次打包;第二,tar.Writer.Close 必须先于底层输出文件关闭,才能写完归档结束块。若要生成 .tar.gz,只需在 tar.NewWriter 外再包一层 gzip writer,但关闭顺序要从内到外完成。

相关问题

为什么 Header.Name 不能直接使用 path?因为 path 可能含有绝对路径或本机分隔符;应基于源目录计算相对路径,再用 filepath.ToSlash 转成 tar 习惯的正斜杠。

FileInfoHeader 会自动保存用户和组吗?它能填充部分文件信息,但用户组名称和系统扩展属性是否完整进入归档,取决于 FileInfo 实现与额外字段配置,不能只凭模式位推断。

为什么解包后权限还是变了?归档头部的模式只是输入,解包工具、umask、目标操作系统和安全策略都可能改变最终权限;应检查解包环境,而不是重复修改打包循环。

版本声明
本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
Go zip 怎样读取归档中的指定文件Go zip 怎样读取归档中的指定文件
上一篇
Go zip 怎样读取归档中的指定文件
Go gzip 怎样给 HTTP 流增加压缩
下一篇
Go gzip 怎样给 HTTP 流增加压缩
查看更多
最新文章
查看更多
课程推荐
  • 前端进阶之JavaScript设计模式
    前端进阶之JavaScript设计模式
    设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
    543次学习
  • GO语言核心编程课程
    GO语言核心编程课程
    本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
    516次学习
  • 简单聊聊mysql8与网络通信
    简单聊聊mysql8与网络通信
    如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
    500次学习
  • JavaScript正则表达式基础与实战
    JavaScript正则表达式基础与实战
    在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
    487次学习
  • 从零制作响应式网站—Grid布局
    从零制作响应式网站—Grid布局
    本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
    485次学习
查看更多
AI推荐
  • PubMedQA数据集详解:生物医学问答基准、功能与应用指南
    PubMedQA
    深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
    582次使用
  • H2O EvalGPT:开源LLM大模型评估与排行榜工具
    H2O EvalGPT
    H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
    620次使用
  • LMArena是什么?伯克利AI模型评估平台使用指南与功能解析
    LMArena
    LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
    654次使用
  • 斯坦福HELM:大语言模型Holistic Evaluation整体评估框架详解
    HELM
    深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
    618次使用
  • MMBench详解:多模态大模型基准测试、功能特点与使用指南
    MMBench
    MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
    435次使用
微信登录更方便
  • 密码登录
  • 注册账号
登录即同意 用户协议 和 隐私政策
返回登录
  • 重置密码