r.maxmultipartmemory 是第一道防线,因其是 gin 解析 multipart 请求前的内存缓冲上限;一旦请求体超限,gin 在解析阶段即返回“http: request body too large”,c.formfile() 根本不执行,故日志无文件名仅见笼统 size 错误。

为什么 r.MaxMultipartMemory 是第一道防线
它不是“限制上传大小”的可选配置,而是 Gin 解析 multipart 请求前的内存缓冲上限。一旦请求体(含所有字段+文件)超过该值,Gin 在解析阶段就直接返回 http: request body too large,c.FormFile() 根本不会被调用——所以你在日志里看不到文件名,只看到笼统的 size 错误。
常见误判:以为设了 MaxMultipartMemory 就万事大吉,结果大文件上传失败却查不到具体 err 来源。
- 默认值是
32 (32 MiB),够小图,不够视频或 ZIP 包 - 必须在
gin.Default()或gin.New()之后、注册任何路由之前设置 - 不能设为
0(不限制),否则恶意请求可能耗尽服务器内存 - 单位是字节,建议按业务最大预期文件设,比如支持 100MB 视频,就写
r.MaxMultipartMemory = 100
c.Request.Body = http.MaxBytesReader() 能补什么漏
r.MaxMultipartMemory 管的是内存解析阶段,而 http.MaxBytesReader 管的是整个请求体的原始字节流读取上限。它能拦截那些绕过 multipart 解析、直接发超大二进制体的攻击(比如伪造 header 的非 multipart 请求)。
但它不替代 MaxMultipartMemory:如果 multipart 请求本身超限,Gin 还是会在解析前就拒掉;而 MaxBytesReader 设太小,可能把合法的大 multipart 请求(含多个字段+大文件)误杀。
- 用法:在 handler 开头加
c.Request.Body = http.MaxBytesReader(c.Writer, c.Request.Body, 100 - 注意:必须在调用
c.FormFile()前设置,否则已读部分无法 rewind - 错误表现是
http: request body too large,和MaxMultipartMemory错误信息一样,但发生阶段不同
前端字段名和 enctype 不匹配,MaxMultipartMemory 再大也没用
很多人调大了 MaxMultipartMemory 却仍收不到文件,根本原因不在大小,而在请求压根没走 multipart 流程。
Gin 只对 Content-Type: multipart/form-data; boundary=... 的请求尝试解析文件。其他编码(如 application/x-www-form-urlencoded)下,c.FormFile() 必然返回 err,MaxMultipartMemory 完全不生效。
- HTML 表单必须显式声明
enctype="multipart/form-data"(拼错成form/multipart就失效) -
<input type="file" name="avatar">的name值,必须和c.FormFile("avatar")的字符串参数完全一致(区分大小写) - curl 测试必须用
-F "avatar=@/path/to/file.jpg",用-d或--data就是普通表单提交
保存前校验文件大小,避免磁盘写满
MaxMultipartMemory 和 MaxBytesReader 都只管“接收”,不管“存盘”。如果攻击者上传一个 500MB 的文件,而你又没检查 file.Size 就直接 c.SaveUploadedFile(),可能把磁盘撑爆。
更糟的是:c.SaveUploadedFile() 内部会先读入内存再写盘,若文件远超 MaxMultipartMemory,它其实已经失败了;但若刚好略小于该值,它就会成功写入——这时你才需要靠 file.Size 拦截。
- 务必在
c.FormFile()成功后,立刻检查file.Size > 100 - 不要依赖前端传来的 hidden 字段做大小校验(可篡改)
- 如果业务允许分片上传,就别硬扛单次大文件;否则至少加个
if file.Size > r.MaxMultipartMemory的兜底判断
MaxMultipartMemory 的设置时机和作用范围——它只影响 Gin 自己的 multipart 解析,对中间件、自定义 Reader、甚至 c.Request.MultipartForm() 的后续操作都不起作用。一旦漏掉这行初始化代码,或者放错位置(比如写在路由注册之后),整个限制就形同虚设。











