静态文件 #
一、静态文件概述 #
1.1 什么是静态文件 #
静态文件是不需要服务器处理的文件,直接返回给客户端:
text
静态文件类型:
├── CSS样式 (.css)
├── JavaScript脚本 (.js)
├── 图片 (.png, .jpg, .gif, .svg)
├── 字体 (.woff, .ttf)
├── 媒体文件 (.mp4, .mp3)
└── 其他 (.ico, .pdf)
1.2 静态文件方法 #
| 方法 | 说明 |
|---|---|
| Static | 映射静态文件目录 |
| StaticFS | 使用http.FileSystem |
| StaticFile | 映射单个文件 |
二、基本静态文件服务 #
2.1 Static方法 #
go
func main() {
r := gin.Default()
// 映射静态文件目录
// 访问 /assets/* 对应 ./assets/* 文件
r.Static("/assets", "./assets")
r.Run()
}
目录结构:
text
project/
├── assets/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── app.js
│ └── images/
│ └── logo.png
└── main.go
访问示例:
text
/assets/css/style.css → ./assets/css/style.css
/assets/js/app.js → ./assets/js/app.js
/assets/images/logo.png → ./assets/images/logo.png
2.2 StaticFile方法 #
go
func main() {
r := gin.Default()
// 映射单个文件
r.StaticFile("/favicon.ico", "./favicon.ico")
r.StaticFile("/robots.txt", "./robots.txt")
r.Run()
}
2.3 StaticFS方法 #
go
func main() {
r := gin.Default()
// 使用http.FileSystem
r.StaticFS("/static", http.Dir("./static"))
r.Run()
}
三、多目录配置 #
3.1 多个静态目录 #
go
func main() {
r := gin.Default()
// CSS目录
r.Static("/css", "./public/css")
// JS目录
r.Static("/js", "./public/js")
// 图片目录
r.Static("/images", "./public/images")
// 上传文件目录
r.Static("/uploads", "./uploads")
r.Run()
}
3.2 嵌套目录 #
go
func main() {
r := gin.Default()
// 一个目录包含所有静态资源
r.Static("/static", "./static")
r.Run()
}
目录结构:
text
static/
├── css/
│ ├── main.css
│ └── admin/
│ └── dashboard.css
├── js/
│ ├── app.js
│ └── admin/
│ └── dashboard.js
└── images/
└── logo.png
访问示例:
text
/static/css/main.css
/static/css/admin/dashboard.css
/static/js/app.js
/static/images/logo.png
四、在模板中使用静态文件 #
4.1 引用CSS #
html
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="/static/css/style.css">
</head>
<body>
<h1>Hello, Gin!</h1>
</body>
</html>
4.2 引用JavaScript #
html
<!DOCTYPE html>
<html>
<body>
<script src="/static/js/app.js"></script>
</body>
</html>
4.3 引用图片 #
html
<img src="/static/images/logo.png" alt="Logo">
4.4 动态路径 #
go
func main() {
r := gin.Default()
r.SetFuncMap(template.FuncMap{
"asset": func(path string) string {
return "/static/" + path
},
})
r.LoadHTMLGlob("templates/*")
r.Static("/static", "./static")
r.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", gin.H{})
})
r.Run()
}
模板中使用:
html
<link rel="stylesheet" href="{{ asset "css/style.css" }}">
<script src="{{ asset "js/app.js" }}"></script>
<img src="{{ asset "images/logo.png" }}">
五、文件缓存 #
5.1 设置缓存头 #
go
func main() {
r := gin.Default()
// 静态文件缓存中间件
r.Use(func(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/static/") {
c.Header("Cache-Control", "public, max-age=31536000")
}
c.Next()
})
r.Static("/static", "./static")
r.Run()
}
5.2 不同文件类型缓存 #
go
func StaticCacheMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
// 根据文件类型设置缓存
switch {
case strings.HasSuffix(path, ".css") || strings.HasSuffix(path, ".js"):
c.Header("Cache-Control", "public, max-age=31536000")
case strings.HasSuffix(path, ".png") || strings.HasSuffix(path, ".jpg") || strings.HasSuffix(path, ".gif"):
c.Header("Cache-Control", "public, max-age=31536000")
case strings.HasSuffix(path, ".html"):
c.Header("Cache-Control", "no-cache")
}
c.Next()
}
}
func main() {
r := gin.Default()
r.Use(StaticCacheMiddleware())
r.Static("/static", "./static")
r.Run()
}
5.3 ETag支持 #
go
func ETagMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
c.Next()
if c.Writer.Status() == 200 {
// 生成ETag
body := c.Writer.(*gin.ResponseWriter)
if body != nil {
hash := md5.Sum(body.Bytes())
etag := hex.EncodeToString(hash[:])
c.Header("ETag", etag)
// 检查客户端ETag
if c.GetHeader("If-None-Match") == etag {
c.Status(304)
}
}
}
}
}
六、文件压缩 #
6.1 Gzip压缩 #
bash
go get github.com/gin-contrib/gzip
go
import "github.com/gin-contrib/gzip"
func main() {
r := gin.Default()
// 启用Gzip压缩
r.Use(gzip.Gzip(gzip.DefaultCompression))
r.Static("/static", "./static")
r.Run()
}
6.2 排除某些文件 #
go
func main() {
r := gin.Default()
r.Use(gzip.Gzip(gzip.DefaultCompression, gzip.WithExcludedExtensions([]string{
".png", ".jpg", ".gif", ".mp4", ".mp3",
})))
r.Static("/static", "./static")
r.Run()
}
七、安全配置 #
7.1 禁止目录浏览 #
go
func main() {
r := gin.Default()
// 自定义静态文件处理
r.NoRoute(func(c *gin.Context) {
path := c.Request.URL.Path
// 防止目录遍历
if strings.Contains(path, "..") {
c.String(400, "Bad Request")
return
}
// 防止访问隐藏文件
if strings.Contains(path, "/.") {
c.String(404, "Not Found")
return
}
c.String(404, "Not Found")
})
r.Static("/static", "./static")
r.Run()
}
7.2 文件类型限制 #
go
func main() {
r := gin.Default()
// 只允许特定文件类型
allowedExts := map[string]bool{
".css": true,
".js": true,
".png": true,
".jpg": true,
".gif": true,
".svg": true,
".woff": true,
".ttf": true,
}
r.Use(func(c *gin.Context) {
if strings.HasPrefix(c.Request.URL.Path, "/static/") {
ext := strings.ToLower(filepath.Ext(c.Request.URL.Path))
if !allowedExts[ext] {
c.String(403, "Forbidden")
c.Abort()
return
}
}
c.Next()
})
r.Static("/static", "./static")
r.Run()
}
八、CDN集成 #
8.1 CDN配置 #
go
type CDNConfig struct {
Enabled bool
BaseURL string
}
func main() {
r := gin.Default()
cdnConfig := CDNConfig{
Enabled: true,
BaseURL: "https://cdn.example.com",
}
r.SetFuncMap(template.FuncMap{
"static": func(path string) string {
if cdnConfig.Enabled {
return cdnConfig.BaseURL + "/static/" + path
}
return "/static/" + path
},
})
r.LoadHTMLGlob("templates/*")
r.Static("/static", "./static")
r.Run()
}
8.2 模板中使用CDN #
html
<link rel="stylesheet" href="{{ static "css/style.css" }}">
<script src="{{ static "js/app.js" }}"></script>
<img src="{{ static "images/logo.png" }}">
8.3 版本控制 #
go
func main() {
r := gin.Default()
version := "1.0.0"
r.SetFuncMap(template.FuncMap{
"static": func(path string) string {
return fmt.Sprintf("/static/%s?v=%s", path, version)
},
})
r.LoadHTMLGlob("templates/*")
r.Static("/static", "./static")
r.Run()
}
模板中使用:
html
<link rel="stylesheet" href="{{ static "css/style.css" }}">
<!-- 输出: /static/css/style.css?v=1.0.0 -->
九、完整示例 #
9.1 项目结构 #
text
project/
├── main.go
├── static/
│ ├── css/
│ │ └── style.css
│ ├── js/
│ │ └── app.js
│ └── images/
│ └── logo.png
└── templates/
└── index.html
9.2 完整代码 #
go
package main
import (
"fmt"
"strings"
"github.com/gin-contrib/gzip"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
// Gzip压缩
r.Use(gzip.Gzip(gzip.DefaultCompression))
// 静态文件缓存
r.Use(StaticCacheMiddleware())
// 模板函数
r.SetFuncMap(template.FuncMap{
"static": func(path string) string {
return "/static/" + path
},
})
// 加载模板
r.LoadHTMLGlob("templates/*")
// 静态文件
r.Static("/static", "./static")
r.StaticFile("/favicon.ico", "./favicon.ico")
// 路由
r.GET("/", func(c *gin.Context) {
c.HTML(200, "index.html", gin.H{
"title": "首页",
})
})
r.Run(":8080")
}
func StaticCacheMiddleware() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
if strings.HasPrefix(path, "/static/") {
ext := strings.ToLower(filepath.Ext(path))
switch ext {
case ".css", ".js", ".png", ".jpg", ".gif", ".svg", ".woff", ".ttf":
c.Header("Cache-Control", "public, max-age=31536000")
case ".html":
c.Header("Cache-Control", "no-cache")
}
}
c.Next()
}
}
9.3 模板文件 #
templates/index.html:
html
<!DOCTYPE html>
<html>
<head>
<title>{{ .title }}</title>
<link rel="stylesheet" href="{{ static "css/style.css" }}">
</head>
<body>
<img src="{{ static "images/logo.png" }}" alt="Logo">
<h1>Hello, Gin!</h1>
<script src="{{ static "js/app.js" }}"></script>
</body>
</html>
十、总结 #
10.1 核心要点 #
| 要点 | 说明 |
|---|---|
| Static | 映射静态文件目录 |
| StaticFile | 映射单个文件 |
| StaticFS | 使用http.FileSystem |
| 缓存 | 设置Cache-Control |
| 压缩 | 使用Gzip |
10.2 最佳实践 #
| 实践 | 说明 |
|---|---|
| 目录组织 | 按类型分目录 |
| 缓存策略 | 静态资源长期缓存 |
| 版本控制 | 使用查询参数 |
| CDN加速 | 使用CDN分发 |
10.3 下一步 #
现在你已经掌握了静态文件服务,接下来让我们学习 数据库概述,了解Gin与数据库的集成!
最后更新:2026-03-28