Go中文件上传限制需在解析阶段介入,通过ParseMultipartForm设置maxMemory(内存阈值)和maxFormSize(总请求体上限)实现;超出maxFormSize直接返回400错误。

在 Go 中实现文件上传限制,关键在于解析 multipart 表单时主动校验,而不是等文件写入磁盘后再判断。Go 的 http.Request.ParseMultipartForm 默认会把整个文件读入内存或临时磁盘,若不加控制,大文件可能耗尽内存或填满磁盘。真正有效的限制需在读取阶段介入。
设置 ParseMultipartForm 的 maxMemory 和 maxFormSize
这是第一道防线。调用 r.ParseMultipartForm 前,必须显式指定内存阈值和总表单大小上限:
-
maxMemory:控制多少字节以内保留在内存(默认 32MB),超过则写入临时磁盘;设为0表示全部写磁盘,但无法阻止超大上传 -
maxFormSize:限制整个 multipart 请求体的总字节数(含文件+字段),单位是字节;超出会直接返回http.ErrMissingFile或 400 错误
示例:
r.ParseMultipartForm(32 r.ParseMultipartForm(0) // 禁用内存缓冲 → 仍无总大小限制✅ 正确做法:
r.ParseMultipartForm(10 r.MultipartReader().NextPart() // 需配合后续校验
手动读取并校验文件头与 MIME 类型
浏览器提交的 Content-Type 可被伪造,仅依赖表单中声明的类型不安全。应在读取文件流时,用前 512 字节检测真实类型:
立即学习“go语言免费学习笔记(深入)”;
- 调用
part.Header.Get("Content-Type")获取客户端声称的类型(参考用) - 用
io.LimitReader(part, 512)读取开头字节,传给http.DetectContentType - 比对白名单(如
"image/jpeg","application/pdf"),不匹配立即拒绝
注意:不要用文件扩展名判断类型,扩展名完全不可信。
边读边计数,实时拦截超大文件
即使设置了 maxFormSize,它只限制整个请求体,而单个文件可能占绝大部分。更稳妥的方式是在读取每个 multipart.Part 时,用 io.TeeReader 或自定义 reader 实时累加已读字节数:
- 初始化计数器
size := int64(0) - 循环读取
part:每次n, err := part.Read(buf)后执行size += int64(n) - 一旦
size > 10 * 1024 * 1024(如 10MB),立刻中断读取、清理资源、返回错误响应 - 避免使用
part.Size字段——它来自 header,可被篡改,不可信
完整流程建议顺序
- 设置合理的
http.Server.ReadTimeout和MaxHeaderBytes防慢速攻击 - 调用
r.ParseMultipartForm并指定maxMemory(如 16MB) - 遍历
r.MultipartReader获取每个 part,跳过非文件字段 - 对每个文件 part:检测真实 MIME、实时计数大小、检查扩展名(辅助)、验证是否在白名单内
- 任一校验失败,立即
http.Error(w, "Invalid file", http.StatusBadRequest)并关闭 part - 全部通过后,再保存或处理文件
不复杂但容易忽略。核心是别让未经校验的数据流进你的处理逻辑。










