静态资源服务与 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 内嵌步骤

  1. //go:embed 指令把目录装进 embed.FS(要求 Go 1.16+)。
  2. 用 fs.Sub 剥掉顶层目录名,让 FS 的根对应要暴露的目录。
  3. 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。
笔记加载中…