embed 是 go 1.16+ 原生嵌入机制,需用 //go:embed 注释紧贴变量声明,类型限 embed.fs 或 string/[]byte;路径相对源文件,支持 和 * 通配符;读取路径区分大小写且无前导斜杠;开发可配合构建标签 -tags dev 动态切换本地读取与嵌入。

embed 是 Go 1.16+ 原生支持的机制,用于在编译时将文件或目录打包进二进制,无需外部依赖。它不运行时读磁盘,也不 require go:generate 或第三方库 —— 只要路径合法、包可见,就能直接用。
嵌入单个文件必须用 //go:embed + 变量声明绑定
不能写成 embed.ReadFile("foo.txt") 直接调用(那是 io/fs 的函数,和 embed 无关);必须先声明一个变量,再用注释绑定路径:
import "embed"
<p>//go:embed hello.txt
var content embed.FS</p><p>data, err := content.ReadFile("hello.txt")
</p>
注意三点:
-
//go:embed注释必须紧贴变量声明前,中间不能有空行或其它语句 - 变量类型只能是
embed.FS或string/[]byte(后者仅限单文件) - 路径是相对于该 Go 文件所在目录的相对路径,不是工作目录
嵌入整个目录需用通配符且变量必须是 embed.FS
想打包 templates/ 下所有 .html 文件?这样写:
//go:embed templates/*.html var templates embed.FS
常见陷阱:
- 通配符只支持
*和**(后者可跨子目录),不支持?或正则 -
templates/目录必须存在,且至少有一个匹配文件,否则编译失败(go build报错pattern matches no files) - 如果目录含子目录,
templates/**才能递归包含;templates/*只匹配一级
embed.FS 读取路径区分大小写且不含前导斜杠
content.ReadFile("index.html") 正确,content.ReadFile("/index.html") 或 content.ReadFile("Templates/index.html") 都会返回 fs.ErrNotExist。
验证路径是否嵌入成功,可用:
files, _ := fs.Glob(templates, "*") // 返回的是不含路径前缀的纯文件名,如 ["a.html", "b.html"]
若需保留目录结构,嵌入时路径写法决定访问方式 —— 比如 //go:embed static/**,那读取就得用 static/css/style.css,而不是 css/style.css。
嵌入资源后无法热更新,调试阶段建议加构建开关
一旦嵌入,文件内容固化在二进制里,改源文件不触发重新嵌入(除非改了 Go 文件本身或执行 go build -a)。开发时容易误以为模板已更新,实际跑的还是旧版本。
推荐做法:用构建标签临时绕过嵌入
//go:build !dev // +build !dev <p>package main</p><p>import "embed"</p><p>//go:embed templates/* var templates embed.FS </p>
然后用 go build -tags dev 编译时跳过 embed,改用 os.ReadFile 读本地文件,方便快速迭代。
真正上线前去掉 -tags dev 即可 —— 这种切换不改逻辑,只改资源加载路径,是最轻量的开发/生产分离方式。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











