必须用c.MultipartForm()获取完整multipart.Form,再从form.File["files"]取[]*multipart.FileHeader切片;它一次性解析全部文件,字段名须与HTML中name严格一致,且不可重复调用。

怎么用 c.MultipartForm() 一次性读取多个同名文件
前端用 提交多文件时,Echo 不支持直接 c.FormFile("files") 拿到多个 —— 它只返回第一个匹配的 *multipart.FileHeader。必须改用 c.MultipartForm() 获取完整结构。
关键点:表单字段名(如 "files")必须和 HTML 中 name 完全一致(大小写敏感),且后端要从 form.File["files"] 取切片,不是单个值。
-
c.MultipartForm()会触发一次 multipart 解析,之后再调用会 panic,所以别在中间件里提前调 - 若前端用了不同 name(如
avatar、cover),就得分别调c.FormFile("avatar")和c.FormFile("cover") - 解析失败常见原因是请求头没带
Content-Type: multipart/form-data; boundary=...,检查前端是否漏了enctype="multipart/form-data"
为什么 file.Open() 后必须流式写入,不能 os.Create + io.WriteString
file.Open() 返回的是一个定位到文件内容起始的只读流,原始二进制结构(比如 PNG 头、PDF 页对象)全在里面。如果用 os.Create() 新建空文件再手动 WriteString(),会把二进制当字符串乱码处理,图片打不开、PDF 报错是必然结果。
正确姿势永远是:io.Copy(dst, src),其中 src 是 file.Open() 的返回值,dst 是 os.OpenFile(..., os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644) 打开的目标文件句柄。
- 务必
defer src.Close()和defer dst.Close(),否则高并发下快速耗尽文件描述符 - 目标路径要先
os.MkdirAll("./uploads", 0755),不然open ./uploads/xxx: no such file or directory - 别直接拼接
file.Filename到路径里,需过滤../、空字符等,防止路径遍历攻击
如何安全校验多个文件的大小和类型
每个 *multipart.FileHeader 都有 Size 字段(真实字节数)和 Header.Get("Content-Type")(客户端声明的 MIME 类型,不可信但可作初筛)。不要依赖扩展名或前端传来的 type 字段。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
BodyLimit 中间件(如 e.Use(middleware.BodyLimit("10M")))限制的是整个请求体,包括所有文件+文本字段;而单个文件大小得靠循环检查 file.Size 来硬控。
- 示例:拒绝单个 > 50MB 的文件 →
if file.Size > 50*1024*1024 { return echo.NewHTTPError(http.StatusBadRequest, "file too large") } - MIME 类型建议白名单校验:
switch file.Header.Get("Content-Type") { case "image/jpeg", "image/png", "application/pdf": ... } - 对关键业务(如用户头像),应额外用
http.DetectContentType()读前几个字节做二次判断,防伪造 Content-Type
并发上传多个文件时最易忽略的资源泄漏点
每个 file.Open() 返回的 src 是一个 *multipart.Part,底层持有对原始请求 Body 的引用。如果没显式 Close(),这部分内存不会释放,且 Go 的 multipart.Reader 在 EOF 前不会自动清理缓冲区。
更隐蔽的问题是:多个文件共用同一个请求 Body,但 file.Open() 每次都重新定位 —— 这本身没问题,前提是每个打开的流都被关闭。一旦漏掉一个 defer src.Close(),轻则 fd 耗尽,重则后续请求 Body 读取异常(http: invalid Read on closed Body)。
生产环境务必用 sync.WaitGroup 或 for range 内统一 defer,别图省事写成 defer file.Open().Close() —— 那样实际执行时机不可控,容易 panic。










