go http处理multipart/form-data上传需调用r.parsemultipartform或r.formfile解析,不可直接读r.body;文件名须校验防路径遍历,扩展名白名单校验,生成唯一文件名并确保目录权限安全。

Go HTTP 处理 multipart/form-data 上传
Go 标准库 net/http 原生支持文件上传,关键在正确解析 multipart/form-data 请求体,而不是当成普通表单。常见错误是直接读 r.Body 或误用 r.FormValue 获取文件字段——这只会拿到文件名字符串,拿不到文件内容。
- 必须调用
r.ParseMultipartForm()(或隐式触发的r.FormFile())来解析 multipart 数据 - 上传字段名(
name属性)需与 HTML 表单中一致,例如<input type="file" name="avatar">对应"avatar" - 默认内存缓冲上限为 32MB,超限会自动写入临时磁盘;如需调整,提前调用
r.ParseMultipartForm(maxMemory)
使用 r.FormFile() 读取单个文件
最简场景(单文件、不关心其他表单字段)直接用 r.FormFile(),它内部会自动调用 ParseMultipartForm 并返回 *multipart.FileHeader。
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, "No file received or parse error: "+err.Error(), http.StatusBadRequest)
return
}
defer file.Close()
// 保存到磁盘(注意:生产环境需校验文件名、类型、大小)
dst, err := os.Create("./uploads/" + header.Filename)
if err != nil {
http.Error(w, "Failed to create file", http.StatusInternalServerError)
return
}
defer dst.Close()
if _, err := io.Copy(dst, file); err != nil {
http.Error(w, "Failed to save file", http.StatusInternalServerError)
return
}
w.WriteHeader(http.StatusOK)
w.Write([]byte("Upload success: " + header.Filename))
}
处理多文件和额外表单字段
当表单含多个文件(<input type="file" name="files" multiple>)或混合文本字段(如 title、category),需显式调用 r.ParseMultipartForm(),再通过 r.MultipartReader() 或循环 r.FormFile() 获取。
部分功能简介:商品收藏夹功能热门商品最新商品分级价格功能自选风格打印结算页面内部短信箱商品评论增加上一商品,下一商品功能增强商家提示功能友情链接用户在线统计用户来访统计用户来访信息用户积分功能广告设置用户组分类邮件系统后台实现更新用户数据系统图片设置模板管理CSS风格管理申诉内容过滤功能用户注册过滤特征字符IP库管理及来访限制及管理压缩,恢复,备份数据库功能上传文件管理商品类别管理商品添加/修改/
-
r.FormValue("title")可安全获取文本字段值(已自动解码) - 对多文件字段,重复调用
r.FormFile("files")直到返回http.ErrMissingFile - 更健壮的做法是用
r.MultipartReader()手动遍历每个Part,区分Content-Disposition中的name和filename - 务必检查
header.Size防止超大文件耗尽内存或磁盘
文件名与路径安全:不要直接使用 header.Filename
header.Filename 来自客户端,可能含 ../、空字节、控制字符或危险扩展名。直接拼接路径会导致目录遍历或覆盖系统文件。
立即学习“go语言免费学习笔记(深入)”;
- 用
path.Base()提取纯文件名,丢弃路径部分 - 白名单校验扩展名(如只允许
.jpg,.pdf),不要依赖header.Header.Get("Content-Type")(易被伪造) - 生成唯一文件名(如
uuid.New().String() + ext),避免同名覆盖或竞争条件 - 保存前确保目标目录存在且权限可控:
os.MkdirAll("./uploads", 0755)
边界情况容易被忽略:空文件、超长文件名、非 UTF-8 编码的原始文件名(某些浏览器会用 GBK)、Content-Type: text/plain 却传二进制内容。这些都得在接收后做二次校验,不能只信 header。









