c.FormFile()不能用于多文件上传,因其仅返回同名字段的第一个multipart.FileHeader,其余全部丢弃;必须用c.MultipartForm()获取map[string][]multipart.FileHeader切片,并校验大小、类型、扩展名,生成唯一安全路径后逐个保存。

GoFiber 中文件上传不是“调个函数就完事”,必须分步处理解析、校验、保存,否则轻则丢文件,重则触发路径遍历或内存溢出。
为什么 c.FormFile() 不能用于多文件上传
c.FormFile("files") 只返回第一个匹配 name 的 *multipart.FileHeader,其余全部丢弃。前端哪怕传了 10 个文件,后端只拿到 1 个。
- 真正批量入口是
c.MultipartForm(),它返回*multipart.Form,其File字段是map[string][]*multipart.FileHeader—— 注意那个切片[] - 前端用
,后端必须写form.File["files"]拿切片,不能只取[0] - 漏掉
c.ParseMultipartForm(32 (如 32MB 上限),c.MultipartForm()可能静默失败,c.FormValue()也拿不到文本字段
如何安全保存多个文件并避免覆盖和路径遍历
直接拼接 "./uploads/" + file.Filename 是高危操作:攻击者可传 ../../etc/passwd 触发路径遍历,或重复上传同名文件导致覆盖。
- 用
uuid.New().String() + filepath.Ext(file.Filename)生成唯一文件名,例如fmt.Sprintf("./uploads/%s%s", uuid.New(), filepath.Ext(file.Filename)) - 保存前必须确保目录存在:
os.MkdirAll("./uploads", 0755),否则首次上传报no such file or directory -
c.SaveFile()是单文件函数,需遍历form.File["files"]切片逐个调用,不能传整个切片
后端必须做的三重校验:大小、类型、扩展名
前端 accept="image/*" 和 JS 校验完全不可信,攻击者可绕过一切前端限制,直接发 multipart 请求。
- 全局内存限制:在
fiber.New()配置中设BodyLimit和ReadBufferSize,避免 OOM;默认 32MB 触发 413 错误 - 单文件大小:用
if file.Size > 10 拦截(单位字节),别依赖Content-Length头 - 类型校验:别信
file.Header.Get("Content-Type"),它可伪造;应打开文件读前 512 字节,用http.DetectContentType()或比对 magic number - 扩展名校验要白名单:
strings.HasSuffix(strings.ToLower(filepath.Base(file.Filename)), ".jpg"),禁用.php、.sh等可执行后缀
真正容易被忽略的是:所有校验必须在 c.SaveFile() 之前完成,且 c.MultipartForm() 的 error 必须显式检查——不处理会导致 panic 或 500,而不是友好的错误响应。











