静态资源服务与 embed 内嵌怎么配
结论先行
三种挂法:r.Static("/static", "./web") 把磁盘目录映射到 URL;r.StaticFile("/favicon.ico", "./x.ico") 挂单个文件;r.StaticFS("/assets", fs) 支持任意 http.FileSystem——配合 //go:embed + fs.Sub + http.FS 即可把静态资源编译进二进制,实现单文件部署。
API 对照
| API | 参数 | 场景 |
|---|---|---|
| r.Static(url, dir) | URL 前缀、磁盘目录 | 开发期/本地磁盘静态资源 |
| r.StaticFile(url, file) | 单文件 | favicon、robots.txt |
| r.StaticFS(url, http.FileSystem) | 任意文件系统 | embed 内嵌、自定义 FS |
embed 内嵌步骤
- 用
//go:embed指令把目录装进 embed.FS(要求 Go 1.16+)。 - 用 fs.Sub 剥掉顶层目录名,让 FS 的根对应要暴露的目录。
r.StaticFS("/static", http.FS(sub))挂载。
代码示例
//go:embed web/static
var staticFS embed.FS
func main() {
r := gin.Default()
// 磁盘目录(开发方便,改动即生效)
r.Static("/public", "./public")
// embed 内嵌:单二进制部署
sub, _ := fs.Sub(staticFS, "web/static") // 去掉 web/static 前缀
r.StaticFS("/static", http.FS(sub))
r.StaticFile("/favicon.ico", "./assets/favicon.ico")
_ = r.Run(":8080")
}
注意事项
- 请求目录路径(以 / 结尾)时,http.FileServer 语义会自动找 index.html;想禁目录列表需要自己包一层 FileServer。
- embed 是一次性的:发布后要改静态资源必须重新编译(这是单二进制的代价)。
- 静态资源量大或追求极致性能时,生产通常交给 Nginx/CDN,Gin 侧只留少量前端壳资源。
- 目录挂载本质是注册了 *filepath 通配路由,注意不要与业务路由冲突。
常见追问 / 记忆点
- 追问:embed 和磁盘目录怎么选?答:追求交付简单/镜像干净用 embed;静态资源频繁更换、希望免发布更新的用磁盘目录或对象存储。
- 追问:为什么 embed 后还要 fs.Sub?答:embed 会保留包相对目录名,FS 根不是目录本身,Sub 后才能让 /static/x 命中目录里的 x。
- 记忆点:Static 目录 / StaticFile 单文件 / StaticFS 任意 FS;embed = go:embed + fs.Sub + http.FS。