maxmultipartmemory 不能限制文件数量,因为它仅控制 multipart 解析阶段内存缓存的总大小,与文件个数或单个文件大小无关;文件数量需通过解析后检查 form.file["files"] 长度并配合文件名净化、空文件过滤等措施实现。

为什么 MaxMultipartMemory 不能限制文件数量
MaxMultipartMemory 只控制 multipart 解析阶段使用的内存上限,和文件个数完全无关。它影响的是表单字段值(包括文件头、文本字段)在内存中缓存的总大小,不是文件个数或单个文件大小的开关。设成 1 或 32 都不会让 Gin 拒绝“上传 100 个空文件”这种请求。
用 c.MultipartForm() 拿到全部文件再计数
真正能拿到本次请求里所有同名文件(比如 <input name="files" multiple>)的方法是 c.MultipartForm(),它返回一个 *multipart.Form,其中 form.File 是 map[string][]*multipart.FileHeader。
- 先调
c.MultipartForm(),失败就直接返回错误(比如解析超时、格式错) - 检查
form.File["files"]的长度,比如限制最多 10 个:if len(form.File["files"]) > 10 - 注意:如果前端没用
multiple,但用户反复提交或脚本伪造多个同名项,form.File["files"]仍可能有多个元素——这正是你要拦的场景 - 不要依赖
c.FormFile("files")循环调用去“凑齐”个数,它内部会移动 reader 偏移,且最后一次返回nil, nil才算结束,逻辑易错
限制前必须确保请求是 multipart 类型
如果请求根本不是 multipart/form-data,c.MultipartForm() 会先触发 ParseMultipartForm(),而这时如果 Content-Type 不对,会直接报错 http: invalid Content-Type,根本走不到你计数那步。
- 前端表单必须带
enctype="multipart/form-data",拼错(如multipart/formdata)就会失败 - 用
curl测试时必须用-F,不能用-d;否则后端收不到Content-Type头,也不会进 multipart 解析流程 - 可以在路由开头加一行日志:
fmt.Println(c.Request.Header.Get("Content-Type")),确认是不是multipart/form-data; boundary=...
文件数量限制要配合路径安全与空文件过滤
只拦数量不够。攻击者可以传 10 个名字全是 ../../etc/passwd 的文件,或者 10 个 Size == 0 的占位符。
- 遍历
form.File["files"]时,每个file都要检查file.Size > 0,否则跳过 - 文件名要做净化:用
filepath.Base(file.Filename)剥离路径,避免写到任意目录 - 保存前确保目标目录存在:
os.MkdirAll("./uploads", 0755),否则c.SaveUploadedFile()会报no such file or directory - 如果业务允许,优先用
file.Open()+io.Copy()替代c.SaveUploadedFile(),便于插校验逻辑(比如图片 magic bytes)
c.MultipartForm(),再检查 len(form.File["files"]) > 10,接着逐个验证 file.Size 和文件名,最后才打开和保存。漏掉任何一环,都可能让限制形同虚设。











