使用 iris.Context.ReadFormFile 或 UploadFormFiles 时需注意:前端表单 enctype 必须为 multipart/form-data;后端需先调用 ParseMultipartForm(隐式触发但默认内存缓冲仅32MB);字段名须匹配;临时文件需手动清理,不可依赖 GC。

直接用 iris.Context.ReadFormFile 或 iris.Context.UploadFormFiles 就能完成基础上传,但绕不开文件大小限制、临时路径清理、MIME校验和并发写冲突这几个坑。
为什么 ctx.ReadFormFile("file") 有时返回空或 panic
常见错误现象:前端 POST 表单没设 enctype="multipart/form-data";后端没调用 ctx.Request().ParseMultipartForm() 就直接读;或者上传字段名不匹配(比如前端传 myfile,代码里写 "file")。
实际使用中必须注意:
-
ParseMultipartForm是隐式触发的,但最大内存缓冲区默认只有 32MB,超限会自动写入磁盘临时目录 —— 这个路径由os.TempDir()决定,不是你可控的 - 如果明确要限制上传大小,应在路由注册前调用
app.MaxRequestBodySize(10 (例如限制 10MB),否则攻击者可构造超大 body 耗尽内存 - 调用
ReadFormFile前,确保请求已通过ctx.Request().Method == "POST"和ctx.GetContentType() == "multipart/form-data"初步校验
UploadFormFiles 多文件上传时如何避免覆盖和权限错误
该函数会把所有同名字段的文件都读进内存(或临时磁盘),返回 []*multipart.FileHeader。容易踩的坑是:直接用 fh.Filename 作为本地保存名 —— 它来自客户端,可能含 ../ 路径遍历、空字节、控制字符,甚至同名文件并发写崩。
安全做法包括:
- 用
path.Base(fh.Filename)截掉路径部分,再用strings.ReplaceAll清除非法字符 - 生成服务端唯一文件名,例如
fmt.Sprintf("%s_%d%s", uuid.NewString(), time.Now().UnixNano(), ext) - 保存前检查目标目录是否存在且可写:
os.MkdirAll(uploadDir, 0755) - 务必用
os.Create而非os.OpenFile(..., os.O_CREATE|os.O_WRONLY),避免因 flag 组合错误导致截断已有文件
如何校验上传文件的真实类型而非依赖 Header
FileHeader.Header.Get("Content-Type") 完全由客户端控制,不可信。比如用户把恶意 exe 改后缀为 .jpg 并伪造 Content-Type: image/jpeg,服务端不做二进制检测就会放行。
推荐做法是读取文件头几个字节比对 magic number:
- 用
f, _ := fh.Open()打开句柄,defer f.Close() - 读前 512 字节:
buf := make([]byte, 512); _, _ = f.Read(buf) - 用
http.DetectContentType(buf)得到推测 MIME,或手动比对常见格式(如 PNG 开头是89 50 4E 47) - 若不匹配白名单(如只允许
image/png,application/pdf),立即return ctx.StatusCode(400).Text("invalid file type")
临时文件没被清理会导致磁盘爆满
当上传体积超过 MaxMemory(默认 32MB)时,net/http 底层会把文件写入 os.TempDir() 下的随机命名临时文件,但 Iris 不负责自动删除 —— 它只给你 FileHeader,后续全靠你自己处理。
这意味着:
- 如果你调用
fh.Open()后没读完内容,临时文件不会被删 - 如果程序 panic 或提前 return,
defer没机会执行,临时文件永久滞留 - Linux 下
/tmp可能被 systemd-tmpfiles 定期清理,但 Windows 或容器环境往往不会
最稳妥的方式是:在成功保存文件后,显式调用 os.Remove(fh.TempFile)(仅当 fh.Size > maxMemory 且 fh.TempFile != "" 时)。别依赖 GC 或系统定时任务。


















