buffalo框架中文件上传需手动启用buffalo.multipart中间件且必须置于路由前,否则c.request().formfile()返回http.errnotmultipart;后端用标准net/http方式读取,前端表单须设enctype="multipart/form-data"、method="post"及匹配的name属性。

Buffalo 框架中文件上传的核心限制
Buffalo 本身不提供开箱即用的 multipart 文件上传解析器,buffalo.Context 的 c.Request().FormFile() 在默认配置下会返回 nil, http.ErrNotMultipart 错误。这不是你代码写错了,而是 Buffalo 的中间件链默认跳过了 multipart/form-data 解析——它把这事交给了开发者显式处理。
必须手动启用 multipart 中间件
在 app.go 的中间件注册位置(通常是 app.Use(...) 区域),你需要插入 buffalo.Multipart 中间件,且必须放在路由匹配之前:
app.Use(buffalo.Multipart) // 后续再挂载其他中间件,比如 session、auth 等 app.Use(middleware.PopTransaction(models.DB))
- 顺序不能错:如果
buffalo.Multipart放在app.Use(routing)之后,请求根本不会走到它,FormFile依然为空 - 它只对
POST或PUT请求中Content-Type: multipart/form-data的请求生效 - 不需要额外 import,它是
github.com/gobuffalo/buffalo自带的
后端接收文件的典型写法
在 handler 中,用标准 net/http 方式读取即可,Buffalo 此时已帮你完成解析:
func UploadHandler(c buffalo.Context) error {
file, header, err := c.Request().FormFile("avatar")
if err != nil {
return c.Error(400, err)
}
defer file.Close()
// 保存到磁盘示例
dst, err := os.Create("./uploads/" + header.Filename)
if err != nil {
return c.Error(500, err)
}
defer dst.Close()
if _, err = io.Copy(dst, file); err != nil {
return c.Error(500, err)
}
return c.Render(200, r.JSON(map[string]string{"status": "ok"}))
}
-
"avatar"是 HTML 表单里<input type="file" name="avatar">的name属性值,必须严格一致 -
header.Size和header.Header.Get("Content-Type")可用于校验大小和类型(如拒绝application/x-executable) - 不要直接用
c.Param("file")或c.FormValue(),它们对文件无效
前端表单注意事项
HTML 表单必须满足三项硬性要求,缺一不可:
- 设置
enctype="multipart/form-data"(仅此一种有效值) - 使用
method="POST"(Buffalo 默认路由不支持PUT表单直传,需额外配置) -
<input type="file">必须有name,且和服务端FormFile("xxx")中的字符串完全匹配
一个最小可用示例:
容易被忽略的是:Buffalo 的 FormFile 不支持同时上传多个同名文件(如 name="photos" 多选),它只返回第一个。如需多文件,得用 c.Request().MultipartReader() 手动遍历 NextPart() —— 这部分逻辑没封装,得自己写。











