vault backup: 2026-05-15 16:26:14

This commit is contained in:
hhs
2026-05-15 16:26:14 +08:00
parent 1ef38fe7e6
commit 0e67673d7a
45 changed files with 5165 additions and 788 deletions
+17 -11
View File
@@ -7,7 +7,7 @@ create time: 2026-04-28 00:00
## 概述
Gin 提供了三种方式提供静态资源:`Static`(目录映射)、`StaticFS`(自定义文件系统)和 `StaticFile`(单文件)。理解它们的区别和使用场景,是构建完整 Web 服务的基础。
Gin 提供了三种核心 API 提供静态资源:`Static`(目录映射)、`StaticFS`(自定义文件系统)和 `StaticFile`(单文件)。此外,通过 `io.Reader` 直接返回文件流以及自定义中间件也是实际项目中常见的手段。理解它们的区别和使用场景,是构建完整 Web 服务的基础。
思考题:为什么生产环境通常不推荐用 Go 直接提供静态文件?Nginx/CDN 相比有什么优势?
@@ -94,9 +94,9 @@ r.StaticFile("/robots.txt", "./static/robots.txt")
r.Run(":8080")
```
### 4. `io.Reader` 直接返回文件流
### 4. 从内存直接返回文件
对于不需要落盘的文件(如数据库读取的图片、动态生成的 PDF),可以直接从 Reader 输出:
对于不需要落盘的文件(如数据库读取的图片、动态生成的 PDF),可以直接从内存输出到响应体:
```go
func getFileFromDB(c *gin.Context) {
@@ -107,14 +107,14 @@ func getFileFromDB(c *gin.Context) {
return
}
// 直接用 io.Reader 写入响应
// DataBytes 直接写入 body,比 SetHeader + Write 更简洁
c.DataBytes(http.StatusOK, contentType, fileData)
// 或者
// c.Writer.Header().Set("Content-Type", contentType)
// c.Writer.Write(fileData)
}
```
> [!tip] Reader vs []byte
> Gin 没有提供专门的 `Reader` 接口写入流式数据。大文件场景建议配合 `c.File()`(内部使用 `sendfile`)或手动实现分块写入,避免一次性加载整个文件到内存。
### 5. 自定义文件服务器
如果需要控制缓存头、限速、访问权限等,可以自己实现 `StaticFS`:
@@ -124,19 +124,22 @@ func secureStatic() gin.HandlerFunc {
return func(c *gin.Context) {
path := c.Request.URL.Path
// 安全检查:防止目录遍历攻击
// 安全检查:确保请求路径在允许的目录范围内
// filepath.Clean 会解析 "..",防止目录遍历攻击
cleanPath := filepath.Clean(path)
if strings.Contains(cleanPath, "..") {
baseDir, _ := filepath.Abs("./static") // 允许的基础目录
targetPath, _ := filepath.Abs(filepath.Join(baseDir, cleanPath))
if !strings.HasPrefix(targetPath, baseDir+"/") && targetPath != baseDir {
c.AbortWithStatus(http.StatusForbidden)
return
}
// 设置缓存头
// 设置缓存和安全头
c.Header("Cache-Control", "public, max-age=31536000") // 一年
c.Header("X-Content-Type-Options", "nosniff")
// 交给内置文件服务器
http.FileServer(http.Dir("./static")).ServeHTTP(c.Writer, c.Request)
http.FileServer(http.Dir(baseDir)).ServeHTTP(c.Writer, c.Request)
}
}
@@ -148,6 +151,9 @@ func main() {
}
```
> [!warning] 常见错误
> 不要这样写:`strings.Contains(path, "..")`。因为 `filepath.Clean()` 已经解析了 `..`,清理后的路径中不会出现 `..`。正确做法是比较**解析后**的绝对路径是否仍在允许的基目录内。
### 6. 静态文件 vs API 性能对比
```mermaid
+1 -1
View File
@@ -157,7 +157,7 @@ func main() {
> **安全警告:** 如果不设置可信代理,攻击者可以伪造 `X-Forwarded-For` 头注入任意 IP,绕过 IP 白名单限流。务必只信任你知道的代理 IP 段。
### 5. 完整的生產環境啟動範例
### 5. 完整的生产环境启动规范
```go
func main() {