必须先调用 ParseMultipartForm 设置内存限制,再调用 FormFile;否则会返回 http.ErrNotMultipart 错误,且未设 MaxMemory 可能导致大文件全载入内存。

如何用 net/http 正确接收 multipart 文件上传
Go 标准库的 net/http 支持原生 multipart 表单解析,但必须显式调用 ParseMultipartForm,否则 r.MultipartForm 为空,r.FormFile 会返回 http.ErrNotMultipart 错误。
常见错误是直接调用 r.FormFile("file") 而没提前解析,或忽略 MaxMemory 设置导致大文件被加载进内存。
ParseMultipartForm(32 表示最多 32MB 内存缓存,超出部分写入临时磁盘文件- 不调用该方法就访问
FormFile,会返回http: multipart handled by ParseMultipartForm类似错误 - 若表单中含非文件字段(如
username),需在ParseMultipartForm后用r.PostFormValue("username")获取
http.HandleFunc 中处理上传并保存到本地文件
核心逻辑是:解析表单 → 获取文件句柄 → 创建目标文件 → 流式拷贝。避免一次性读入内存,尤其对视频、压缩包等大文件。
注意 multipart.File 是 io.Reader,可直接传给 io.Copy;目标路径需确保父目录存在,否则 os.Create 会失败。
立即学习“go语言免费学习笔记(深入)”;
func uploadHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
err := r.ParseMultipartForm(32 << 20)
if err != nil {
http.Error(w, "Unable to parse form", http.StatusBadRequest)
return
}
file, header, err := r.FormFile("file")
if err != nil {
http.Error(w, "No file received", http.StatusBadRequest)
return
}
defer file.Close()
dst, err := os.Create("./uploads/" + header.Filename)
if err != nil {
http.Error(w, "Cannot 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"))
}
为什么 http.ServeFile 不适合直接提供上传后文件访问
http.ServeFile 默认不支持目录列表,且将 ./uploads/ 直接暴露为根路径易引发路径遍历风险(如请求 ../../etc/passwd)。更安全的做法是用 http.StripPrefix + http.FileServer 限定子路径,并禁用目录索引。
- 错误写法:
http.HandleFunc("/files/", http.ServeFile)—— 参数不匹配,ServeFile不是HandlerFunc - 正确做法:用
http.FileServer(http.Dir("./uploads"))并包裹StripPrefix - 必须手动创建
./uploads目录,否则首次访问返回 404
前端 HTML 表单必须满足的三个条件
后端能正常接收的前提是前端构造合法 multipart 表单。漏掉任一条件都会导致 FormFile 返回空或报错。
-
enctype="multipart/form-data"必须显式声明,application/x-www-form-urlencoded无法传二进制 -
的name值要和后端r.FormFile("file")中字符串完全一致 - 不能用
fetch或axios发送纯 JSON,必须用FormData构造(即使只传一个文件)
容易被忽略的是:开发时用 curl 测试,必须加 -F "file=@/path/to/file",而不是 -d。










