Go Web 文件上传必须服务端校验:先用 header.Size 和 io.LimitReader 限制大小,再读取 magic bytes 验证格式(如 JPEG 为 0xFF 0xD8),最后清洗文件名防路径遍历。
在 Go Web 开发中,表单文件上传的验证(如限制格式、大小)不能只靠前端,必须在服务端严格校验。Golang 本身不提供开箱即用的文件验证中间件,但借助标准库 net/http 和基础 I/O 操作,可以安全、可靠地实现。
浏览器上传文件时通常使用 multipart/form-data 编码。Go 的 req.ParseMultipartForm() 会将文件暂存到内存或磁盘,但**在读取全部内容前就应完成基础校验**,避免恶意大文件耗尽资源。
关键做法是:先调用 req.MultipartReader() 或直接使用 req.FormFile() 获取 *multipart.FileHeader,它包含文件名、大小(Size)和原始头信息(可用于检查 MIME 类型)。
Header.Size 是客户端声明的文件大小(可被伪造,仅作初步过滤)Header.Header.Get("Content-Type") 提供 MIME 类型(同样不可信,需后续校验)必须在读取文件流前设置最大允许字节数,防止 DoS 攻击。推荐两种方式:
http.Server 中设置 MaxRequestBodySize(Go 1.19+)或使用 http.MaxBytesReader 包裹响应体io.LimitReader(file, maxSize) 封装文件流,后续读取超过限制会返回 io.EOF
示例片段:
maxSize := int64(5 * 1024 * 1024) // 5MB仅检查扩展名(如 .jpg)或 Content-Type 极不安全。应读取文件开头数个字节,比对已知魔数(Magic Number):
0xFF 0xD8 开头0x89 0x50 0x4E 0x47 开头0x47 0x49 0x46("GIF")开头0x25 0x50 0x44 0x46("%PDF")开头操作建议:
io.ReadFull(file, buf[:n]) 安全读取前 N 字节(避免 panic)buf 转为 []byte 后
用 bytes.Equal() 或 bytes.HasPrefix() 匹配file.Seek(0, 0))以便后续保存完整流程应按「大小 → 格式 → 扩展名(辅助)」顺序校验,任一失败立即终止并返回清晰错误(如 HTTP 400)。不要合并多个检查逻辑到一个 if 中,便于定位问题。
常见疏漏点:
file 导致句柄泄漏header.Filename 直接拼路径,引发目录遍历(应使用 path.Base() 清洗)http.Error() 的正确状态码(如 413 Payload Too Large)不复杂但容易忽略。