c.FormFile("avatar")可获取文件元信息,需前端enctype="multipart/form-data"且name匹配;须调file.Open()读取数据流,校验大小、类型并安全保存。

怎么用 c.FormFile() 拿到上传的文件
直接调 c.FormFile("avatar") 就行,前提是前端表单用了 enctype="multipart/form-data",且 的 name 和参数完全一致(大小写敏感)。它返回的是 *multipart.FileHeader,只含元信息(文件名、大小、头),不含实际数据流。
常见错误包括:
- 前端用
application/json发 base64 字符串,后端却调c.FormFile()→ 返回echo.ErrMissingFile - 字段名不匹配(比如前端是
profile_pic,后端写成"avatar")→ 返回nil+echo.ErrNotFound - 在中间件里提前读了
c.Request.Body→ 后续c.FormFile()失败,因为 Body 已被消耗
拿到 FileHeader 后怎么安全读取并保存
file.Open() 才真正打开数据流,必须调用它;跳过这步直接 os.Create() 写空文件,会导致二进制损坏(图片/PDF 打不开)。
实操要点:
- 先用
os.MkdirAll("./uploads", 0755)确保目录存在,否则open ./uploads/xxx: no such file or directory - 对
file.Filename做净化:filepath.Base(file.Filename)去掉路径部分,再用正则过滤非法字符(如../、\x00) - 别复用原名,生成唯一文件名:
uuid.New().String() + filepath.Ext(file.Filename) - 用
io.Copy(dst, src)写入,不是dst.Write(buf);写完务必src.Close()和dst.Close()
怎么限制大小、类型和防攻击
middleware.BodyLimit("5M") 是第一道防线,但它限制的是整个请求体(含所有表单字段),不是单个文件。攻击者可塞一堆文本字段+一个超大文件绕过。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
必须手动校验:
- 检查
file.Size—— 这是真实字节数,比Content-Length或 header 声明更可靠 - 用
filepath.Ext(file.Filename)判断扩展名,比file.Header.Get("Content-Type")可靠(后者可伪造) - 若需强类型校验,用
net/http/sniff读前 512 字节做 MIME 探测 - 上传目录不能是 Web 根目录,避免用户通过 URL 直接访问恶意文件
为什么不能直接用 file.Size 做最终校验
file.Size 是客户端声明的大小,可被篡改。上传过程中网络中断或客户端异常终止时,实际读取字节数可能小于该值,但 io.Copy() 不会自动报错。
安全做法是边读边累加计数,最后对比总长:
var total int64
for {
n, err := src.Read(buf)
total += int64(n)
if err == io.EOF { break }
if err != nil && err != io.ErrUnexpectedEOF { return err }
}
if total != file.Size { return errors.New("file size mismatch") }
这个细节常被忽略:框架不帮你做完整性校验,你得自己兜底。










