第20章:静态文件与文件上传
本章目标
学完本章后,你将能够:
- 使用
r.Static()提供静态文件服务 - 处理单文件和多文件上传
- 限制文件大小和类型
- 实现文件下载接口
前置知识
- 需要先学习:第06章(表单参数)、第07章(请求头)
20.1 静态文件服务
go
// 提供整个目录(URL 路径映射到本地目录)
r.Static("/static", "./public")
// http://localhost:8080/static/css/style.css → ./public/css/style.css
// 提供单个文件
r.StaticFile("/favicon.ico", "./public/favicon.ico")
// 自定义文件系统
r.StaticFS("/assets", http.Dir("./assets"))20.2 单文件上传
go
r.POST("/upload", func(c *gin.Context) {
// 1. 获取上传的文件
file, err := c.FormFile("file")
if err != nil {
c.JSON(400, gin.H{"error": "请选择文件"})
return
}
// 2. 保存到磁盘
dst := "./uploads/" + file.Filename
if err := c.SaveUploadedFile(file, dst); err != nil {
c.JSON(500, gin.H{"error": "保存失败"})
return
}
c.JSON(200, gin.H{
"message": "上传成功",
"filename": file.Filename,
"size": file.Size,
})
})bash
curl -X POST http://localhost:8080/upload \
-F "file=@/path/to/image.png"20.3 多文件上传
go
r.POST("/upload-multi", func(c *gin.Context) {
form, err := c.MultipartForm()
if err != nil {
c.JSON(400, gin.H{"error": err.Error()})
return
}
files := form.File["files"] // "files" 是表单字段名
var uploaded []string
for _, file := range files {
dst := "./uploads/" + file.Filename
c.SaveUploadedFile(file, dst)
uploaded = append(uploaded, file.Filename)
}
c.JSON(200, gin.H{
"message": fmt.Sprintf("成功上传 %d 个文件", len(uploaded)),
"files": uploaded,
})
})bash
curl -X POST http://localhost:8080/upload-multi \
-F "files=@img1.png" \
-F "files=@img2.jpg" \
-F "files=@doc.pdf"20.4 文件大小限制
go
// 全局限制:最大 8MB
r.MaxMultipartMemory = 8 << 20 // 8MB
// 或者用中间件限制请求体大小
r.Use(func(c *gin.Context) {
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 10<<20)
c.Next()
})20.5 文件类型验证
go
func allowedFileType(file *multipart.FileHeader) bool {
allowed := map[string]bool{
"image/jpeg": true,
"image/png": true,
"image/gif": true,
"application/pdf": true,
}
contentType := file.Header.Get("Content-Type")
return allowed[contentType]
}20.6 文件下载
go
// 方式一:直接返回文件
c.File("./files/report.pdf")
// 方式二:带下载文件名
c.FileAttachment("./files/report.pdf", "2024年度报告.pdf")
// 方式三:从内存返回
data := []byte("文件内容")
c.Data(http.StatusOK, "application/octet-stream", data)常见错误
错误1:忘记创建 uploads 目录
go
// ❌ uploads 目录不存在 → 保存失败
c.SaveUploadedFile(file, "./uploads/"+file.Filename)
// ✅ 先确保目录存在
os.MkdirAll("./uploads", 0755)错误2:静态文件路径和路由冲突
go
// ❌ /static 既是静态文件又是路由
r.GET("/static", handler)
r.Static("/static", "./public") // 冲突!错误3:大文件上传默认存内存
超过 MaxMultipartMemory 的文件会存到临时文件,但小于它的全放内存——如果同时很多人上传 5MB 文件,内存可能爆。
本章小结
r.Static("/url", "./local")提供静态文件c.FormFile("field")+c.SaveUploadedFile()处理上传c.MultipartForm()批量处理多文件- 文件大小用
MaxMultipartMemory或MaxBytesReader限制 c.File()/c.FileAttachment()实现下载
练习题
- 配置静态文件服务,将
./public目录映射到/static路径。 - 实现单文件上传接口,保存到
./uploads/目录。 - 实现多文件上传接口,限制最多上传 5 个文件。
- 实现文件下载接口,根据 URL 参数返回不同的文件。
- (思考题)大文件(>100MB)上传时直接存内存会有什么问题?如何优化?