Beego上传文件核心是正确使用GetFile(单个)和GetFiles(多个),它们自动解析multipart数据并返回File和FileHeader,无需手动处理流或边界;GetFile参数名须与HTML表单name严格一致,h.Filename需用path.Base清洗以防范路径遍历,SaveToFile第二参数为完整路径且目录须预先存在,多文件需遍历GetFiles结果并逐个Open/Close,务必校验文件名合法性、扩展名及内容类型,配合MaxUploadSize和MaxBytesReader实现安全限流。

Beego 上传文件的核心是用对两个方法:GetFile(单个)和 GetFiles(多个),它们直接返回已解析的 multipart.File 和 multipart.FileHeader,不用自己读流、解包或处理边界——这点比原生 net/http 省心太多。
单个文件上传:用 GetFile + SaveToFile
这是最常用场景,比如头像上传、配置文件提交。关键不是“能不能传”,而是“怎么避免覆盖、乱码、路径越界”。
-
GetFile("myfile")的参数名必须和 HTML 表单中<input type="file" name="myfile">的name严格一致,大小写敏感 -
h.Filename是原始文件名(含扩展名),但可能带路径(如C:\fakepath\report.pdf),务必用path.Base(h.Filename)提取干净文件名 -
SaveToFile("myfile", fpath)第一个参数也要和表单name一致;第二个参数是**绝对路径或相对于项目根目录的路径**,不能是相对路径如./upload/xxx - 如果想保留原始文件名,直接拼接
path.Base(h.Filename)即可,但要校验合法性(空字符、../、控制字符等)
多个文件上传:用 GetFiles 遍历处理
浏览器端需加 multiple 属性:<input type="file" name="myfiles" multiple>。后端拿到的是 []*multipart.FileHeader 切片,每个元素都带独立的 Filename 和 Size。
-
GetFiles("myfiles")返回的切片长度就是用户选中的文件数,为空则说明没选或前端没发过去 - 每个文件都要单独调用
file.Open()获取可读句柄,且必须defer file.Close(),否则临时文件不清理,磁盘会悄悄占满 - 别用
SaveToFile批量保存——它只支持单个name,多个文件得手动io.Copy(dst, file)或用os.WriteFile - 注意并发安全:如果用 goroutine 并行保存,确保目标目录已存在(
os.MkdirAll要在循环外或加锁)
文件名与路径安全:别信前端传来的任何路径信息
h.Filename 可被任意篡改,比如传 ../../../etc/passwd 或空字符串。Beego 不做自动过滤,这活得你来干。
- 永远用
path.Base(h.Filename)剥离路径,再检查是否为空、是否含非法字符(如\0、/、..) - 扩展名验证不能只看后缀,要用
mime.TypeByExtension或读文件头(magic number)——否则用户把木马改成.jpg就能绕过 - 保存路径建议固定前缀(如
static/uploads/),并用time.Now().Format("2006/01/02")分目录,避免单目录下文件过多影响性能 - 生成唯一文件名更稳妥(如
uuid.NewString() + ext),但若业务强依赖原始名(如合同扫描件),就只能清洗后保留
上传限制与错误响应:413 不是偶然发生的
Beego 默认允许最大 256MB 上传(web.MaxUploadSize = 1),但内存缓存只有 64MB(<code>web.MaxMemory = 1)。超限直接返回 HTTP 413,不会进你的控制器逻辑。
- 调整限制要在
main.go初始化阶段设置:web.MaxUploadSize = 10 - 不要在控制器里 try-catch 413 错误——它根本到不了你那,得靠 Nginx 或 Beego 全局中间件拦截
-
GetFile或GetFiles返回 error 时,通常是解析失败(损坏 multipart 包)或超限被截断,此时h可能为nil,务必判空 - 客户端看到空白响应或 500,大概率是没处理
err != nil就继续用了f或h
真正麻烦的不是上传动作本身,而是后续的文件名清洗、类型校验、存储路径构造和错误分支覆盖——这些地方漏一条,上线后就可能被扫出路径遍历或任意文件写入。


















