iris.context.readformfile 读不到文件是因为前端未正确发送 multipart/form-data 请求,需确保表单 enctype 正确、name 匹配,且必须先调用 ctx.formfile 触发解析;大文件需调小 maxrequestbodysize 并检查反向代理限制,保存前须过滤文件名防路径遍历。

iris.Context.ReadFormFile 读不到文件?检查 multipart 表单和请求头
直接调用 ctx.ReadFormFile("file") 返回空或报错,大概率不是 Iris 问题,而是前端没发对。Iris 的文件上传完全依赖标准 HTTP multipart/form-data 协议,它不会自动解析 application/json 或 URL-encoded 数据里的文件字段。
必须确保:
- HTML 表单带
enctype="multipart/form-data",且<input type="file" name="file">的name和后端ReadFormFile参数一致 - 用 fetch / axios 上传时,不能手动设置
Content-Type—— 浏览器需要自动生成 boundary,设死会导致服务端无法解析 multipart - Postman 测试时,选择
form-data类型,Key 填file,Value 选文件,不要切到binary或raw
保存文件前必须调用 ctx.FormFile
ctx.ReadFormFile 只是读取内存中的文件句柄,不校验文件是否存在或是否合法;真正触发解析、校验和临时文件生成的是 ctx.FormFile。漏掉这步,后续操作会 panic 或返回 nil。
典型写法:
file, info, err := ctx.FormFile("file")
if err != nil {
ctx.StatusCode(400)
ctx.WriteString("upload error: " + err.Error())
return
}
defer file.Close() // 必须 defer,否则可能锁住临时文件
<p>dst, err := os.Create("./uploads/" + info.Filename)
if err != nil {
ctx.StatusCode(500)
return
}
defer dst.Close()</p><p>_, err = io.Copy(dst, file)
</p>
注意:info.Size 是真实上传大小,可用于限制(比如拒绝 >10MB 的文件),而 ctx.FormValue("xxx") 拿不到文件字段值 —— 它只处理文本字段。
大文件上传要调小 iris.Application.MaxRequestBodySize
Iris 默认限制请求体为 32MB(iris.Configuration{MaxRequestBodySize: 33554432}),超过直接 413。但这个值不是“文件大小上限”,而是整个 HTTP 请求体(含表单字段、boundary、换行等)的总长。
如果上传 50MB 文件却卡在 413,别急着改大,先确认:
- 前端是否启用了分片上传?单次请求超限是设计使然
- 反向代理(Nginx/Caddy)是否也设了
client_max_body_size或request_body_max_size?它会先于 Iris 拦截 - 改配置时用字节数,不是字符串如
"50MB"—— 写成52428800或50 * iris.MB
ctx.SaveUploadedFile 适合简单场景,但不处理重名和路径遍历
ctx.SaveUploadedFile(file, "./uploads/"+filename) 看似方便,但它不做任何安全过滤。攻击者传 ../../../etc/passwd 这类 filename 会直接写入系统关键路径。
务必自行清理文件名:
filename := info.Filename
// 只保留字母、数字、下划线、短横线和点
re := regexp.MustCompile(`[^a-zA-Z0-9._-]+`)
filename = re.ReplaceAllString(filename, "_")
<p>// 强制加随机前缀防重名
filename = fmt.Sprintf("%s_%s", uuid.New().String()[:8], filename)</p><p>err := ctx.SaveUploadedFile(file, "./uploads/"+filename)
</p>
另外,SaveUploadedFile 内部仍调用 io.Copy,所以大文件依然吃内存 —— 真正流式处理(不落地临时文件)需用 ctx.Request().MultipartReader() 手动解析,但绝大多数业务没必要这么复杂。











