GoFiber 中多文件上传需调用 c.MultipartForm() 获取 map[string][]*multipart.FileHeader,再遍历 form.File["name"] 切片逐个保存,须校验错误、清洗文件名、创建目录并重验类型与大小。

GoFiber 中 c.MultipartForm() 是多文件上传的核心入口
GoFiber 不像 Express 那样提供开箱即用的 req.files 对象,它把表单解析和文件提取拆得更细。真正拿到多个文件,必须调用 c.MultipartForm(),而不是只用 c.FormFile()(后者只返回第一个匹配 name 的文件)。这个函数返回一个 *multipart.Form,其 File 字段是 map[string][]*multipart.FileHeader —— 注意是切片,这才是批量的关键。
- 如果前端用
<input type="file" name="files" multiple>,后端必须通过form.File["files"]拿到文件切片,不能写成form.File["files"][0]就完事 -
c.FormFile("files")只取第一个,其余丢弃,这在批量场景下等于直接丢数据 - 务必检查
err:c.MultipartForm()可能因请求体过大、解析失败或超时返回错误,不处理会导致 panic 或 500
保存多个文件时,c.SaveFile() 要逐个调用,且路径需唯一
c.SaveFile() 本身不支持批量保存,它只接受单个 *multipart.FileHeader 和目标路径。这意味着你必须遍历文件切片,为每个文件生成独立路径——否则后一个会覆盖前一个。
- 别直接拼接
./uploads/ + file.Filename:用户可能上传同名文件,或构造恶意路径(如../../etc/passwd),必须做安全清洗 - 推荐用
uuid.New().String()+ 原扩展名生成新文件名,例如fmt.Sprintf("./uploads/%s%s", uuid.New(), filepath.Ext(file.Filename)) - 保存前检查
os.Stat()目录是否存在,os.MkdirAll("./uploads", 0755)不能少,否则首次上传必报错
文件验证不能只靠前端,后端必须重验类型、大小、内容
前端 accept 和 JS 校验形同虚设。攻击者可绕过一切前端限制,直接发 multipart 请求。Fiber 层面必须在读取文件头或保存前做硬性拦截。
- 大小限制:用
c.Locals("maxMultipartMemory", 32 在启动时全局设上限(单位字节),避免 OOM;再用 <code>file.Size > 10<<20单文件校验 - 类型校验:不要信
file.Header.Get("Content-Type"),它可被伪造;应读取文件前几个字节比对 magic number(比如用file.Open()+io.ReadFull()) - 扩展名校验要白名单:
strings.HasSuffix(strings.ToLower(file.Filename), ".jpg"),禁用.php、.js等可执行后缀
并发保存多个文件时,SaveFile 是阻塞操作,别盲目加 goroutine
GoFiber 的 c.SaveFile() 底层调用 os.Create + io.Copy,属于同步 I/O。如果在循环里起 goroutine 并发保存,看似快了,但极易触发系统级资源耗尽(太多打开文件句柄、磁盘 IOPS 打满),尤其在高并发上传场景下。
- 优先考虑顺序保存 + 提升单次 I/O 效率:用
os.O_CREATE | os.O_WRONLY | os.O_TRUNC标志,避免 append 模式反复 seek - 真要并发,必须加限流:用
semaphore.NewWeighted(3)控制同时最多 3 个文件写入,防止雪崩 - 更稳妥的做法是保存到内存 buffer 后统一落盘,或接入对象存储(如 S3),把 I/O 压力转给专业服务
MultipartForm 解析的内存阈值和临时目录清理。Fiber 默认把整个 multipart body 缓存在内存或临时磁盘,若没配 Config{MaxRequestBodySize: ...} 和 ReadBufferSize,小流量测试没问题,上线后遇到几百 MB 的 ZIP 包上传,服务会直接卡死或 OOM。


















