Web 应用少不了静态资源:CSS、JavaScript、图片、字体等。Gin 提供了几种方式来服务静态文件。
最简单的方式是用 Static 方法:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Static("/assets", "./static")
r.Run(":8080")
}
这样配置后,访问 /assets/css/style.css 就会返回 ./static/css/style.css 文件。
如果只需要服务单个文件,比如 favicon:
r.StaticFile("/favicon.ico", "./static/favicon.ico")
StaticFS 方法可以更灵活地控制静态文件服务:
r.StaticFS("/static", gin.Dir("./static", true))
第二个参数 true 表示列出目录内容,生产环境建议设为 false。
也可以使用 Go 标准库的 http.Dir:
import "net/http"
r.StaticFS("/static", http.Dir("./static"))
Go 1.16 引入了 embed 包,可以把静态文件嵌入到二进制文件中:
package main
import (
"embed"
"net/http"
"github.com/gin-gonic/gin"
)
//go:embed static/*
var staticFS embed.FS
func main() {
r := gin.Default()
r.StaticFS("/static", http.FS(staticFS))
r.Run(":8080")
}
这样部署时就不需要带着 static 目录了,所有文件都编译进了二进制。
一个典型的静态文件目录:
static/
├── css/
│ ├── style.css
│ └── admin.css
├── js/
│ ├── main.js
│ └── vendor.js
├── images/
│ ├── logo.png
│ └── icons/
│ └── home.svg
└── fonts/
└── custom.woff2
静态文件和 HTML 模板配合使用:
r.LoadHTMLGlob("templates/*")
r.Static("/static", "./static")
r.GET("/", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", gin.H{
"title": "首页",
})
})
模板中引用静态资源:
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<img src="/static/images/logo.png" alt="Logo">
<script src="/static/js/main.js"></script>
</body>
</html>
静态资源通常需要设置缓存策略:
r.Use(func(c *gin.Context) {
if len(c.Request.URL.Path) >= 7 && c.Request.URL.Path[:7] == "/static" {
c.Header("Cache-Control", "public, max-age=31536000")
}
c.Next()
})
r.Static("/static", "./static")
或者使用中间件:
func CacheMiddleware(duration time.Duration) gin.HandlerFunc {
return func(c *gin.Context) {
c.Header("Cache-Control", fmt.Sprintf("public, max-age=%d", int(duration.Seconds())))
c.Next()
}
}
r.Use(CacheMiddleware(24 * time.Hour))
r.Static("/static", "./static")
默认情况下 Gin 会安全处理路径,但最好还是检查一下:
r.NoRoute(func(c *gin.Context) {
c.String(http.StatusNotFound, "页面不存在")
})
如果需要从数据库或其他来源提供静态文件:
type VirtualFS struct {
files map[string][]byte
}
func (v *VirtualFS) Open(name string) (http.File, error) {
content, ok := v.files[name]
if !ok {
return nil, os.ErrNotExist
}
return &VirtualFile{content: content, name: name}, nil
}
r.StaticFS("/virtual", &VirtualFS{
files: map[string][]byte{
"hello.txt": []byte("Hello from virtual FS"),
},
})
Gin 的静态文件服务简单易用,Static 方法能满足大部分需求。如果需要嵌入静态文件到二进制,使用 Go 的 embed 包很方便。记得给静态资源设置合适的缓存策略,可以显著提升性能。