使用 ctx.ReadFormFile("file") 仅取首个同名文件且无校验,应改用 ctx.FormFile;多文件需遍历 ctx.MultipartForm().File[name] 并逐个 Open;务必 sanitize 文件名防路径遍历,且须配置 iris.WithPostMaxMemory 防 OOM。

iris.Context.ReadFormFile 读取单个文件时要注意什么
直接用 ctx.ReadFormFile("file") 是最简方式,但它只返回第一个同名 input 的文件(即使前端传了多个),且不校验 Content-Type 或大小。实际上传中容易因前端没设 enctype="multipart/form-data" 导致读取失败,错误信息是 http: no such file。
推荐改用 ctx.FormFile("file"),它会解析整个 multipart 表单,并支持多值:
file, header, err := ctx.FormFile("file")
if err != nil {
ctx.StatusCode(400)
ctx.WriteString("读取文件失败: " + err.Error())
return
}
defer file.Close()
<p>dst, err := os.Create("./uploads/" + header.Filename)
if err != nil {
ctx.StatusCode(500)
ctx.WriteString("保存失败")
return
}
defer dst.Close()</p><p>_, err = io.Copy(dst, file)
</p>
注意:header.Filename 是原始文件名,不可直接拼路径——需过滤 ../、空字节等,否则有路径遍历风险。
iris 处理多文件上传的正确姿势
前端用 <input type="file" name="files" multiple> 时,name 相同但会发多个 part。Iris 不提供类似 ctx.FormFiles("files") 的批量接口,必须手动遍历 ctx.MultipartForm() 的 File 字段:
-
ctx.MultipartForm()返回*multipart.Form,其File是map[string][]*multipart.FileHeader - 键是
input name,值是该 name 对应的所有文件头切片 - 每个
*multipart.FileHeader需单独调用ctx.Request().MultipartReader()获取对应文件流(不能复用同一个 reader)
实操建议:先遍历拿到所有 FileHeader,再逐个打开并保存:
form, err := ctx.MultipartForm()
if err != nil {
ctx.StatusCode(400)
return
}
<p>files := form.File["files"]
for _, fh := range files {
file, err := fh.Open()
if err != nil {
continue // 跳过单个失败项,不中断整体
}
defer file.Close()</p><pre class="brush:php;toolbar:false;">dst, _ := os.Create("./uploads/" + sanitizeFilename(fh.Filename))
_, _ = io.Copy(dst, file)
dst.Close()}
sanitizeFilename 必须做——Go 标准库不处理文件名编码,中文或特殊字符可能在某些系统出错;建议用正则替换非字母数字和下划线、点、短横线的字符,再截断长度。
上传大文件或并发多文件时的内存与超时控制
Iris 默认不限制 multipart 解析内存,大文件会全载入内存再处理,极易 OOM。必须显式配置 iris.WithPostMaxMemory(32 (32MB):
context deadline exceeded
-
ctx.SetReadTimeout(5 * time.Minute)在 handler 开头调用,仅对当前请求生效 - 避免全局设过长 timeout,防止连接堆积
- 生产环境建议加进度回调或分块上传,Iris 本身不内置分块支持
保存文件前必须做的三件事
跳过任意一项都可能导致安全漏洞或运行时崩溃:
- 检查
fh.Size是否超过业务允许上限(如 10MB),超限直接拒绝,不调用fh.Open() - 用
mime.TypeByExtension或http.DetectContentType校验文件真实类型,不能只信fh.Header.Get("Content-Type")(前端可伪造) - 生成唯一文件名(如
uuid.New().String() + filepath.Ext(fh.Filename)),避免覆盖或冲突;原名仅用于记录日志或数据库字段
真正麻烦的是 mime 类型校验——http.DetectContentType 只读前 512 字节,对小文件有效,但对 zip、pdf 等需更多字节判断的格式可能不准。线上建议结合扩展名白名单 + 文件头 magic number 判断,而不是完全依赖 Go 标准库。











