Go标准库需先调用r.ParseMultipartForm(32

用 http.HandleFunc 接收 multipart/form-data 文件
Go 标准库原生支持表单文件上传,关键在正确解析 multipart/form-data。必须先调用 r.ParseMultipartForm(或 r.ParseForm),否则 r.MultipartForm 为 nil,读 r.FormFile 会 panic。
常见错误:直接调 r.FormFile("file") 而没 parse,报错 http: no such file 或空 *multipart.FileHeader。
- 建议设置内存阈值:
r.ParseMultipartForm(32 (32MB),避免小文件全进内存,大文件自动落盘 -
r.FormFile("file")返回multipart.File和*multipart.FileHeader,后者含Filename、Size、Header等元信息 - 务必检查返回 error,比如用户没选文件时,
http.ErrMissingFile是合法错误,不应当 panic 处理
保存文件前必须校验 FileHeader.Size 和 Filename
不校验就写磁盘 = 给攻击者留后门。典型风险包括路径遍历(../../etc/passwd)、超大文件打爆磁盘、空文件名导致 open 失败。
- 用
filepath.Base(fh.Filename)提取原始文件名,丢弃路径部分 - 检查
fh.Size是否超过业务允许上限(如 100MB),避免io.Copy时 OOM - 过滤危险扩展名(如
.sh、.exe),但注意 MIME 类型可伪造,仅作辅助判断 - 生成安全文件名:用
uuid.New().String()+ 原扩展名,避免中文/特殊字符引发 fs 错误
用 os.Create 写入比 ioutil.WriteFile 更可控
ioutil.WriteFile(或 os.WriteFile)会一次性加载整个文件到内存再写,对大文件极不友好;而流式写入能控速、可中断、内存恒定。
立即学习“go语言免费学习笔记(深入)”;
- 打开目标文件:
f, err := os.Create(destPath),记得defer f.Close() - 用
io.CopyN(f, src, fh.Size)或带限速的io.Copy,防止瞬间打满 I/O - 写完调
f.Sync()确保落盘(尤其日志类关键文件),再os.Chmod(f.Name(), 0644)设权限 - 别忽略
src.Close()——multipart.File是io.ReadCloser,漏关可能卡住连接复用
前端 必须配 enctype="multipart/form-data"
这是最容易被忽略的 HTML 配置。如果 form 没设 enctype,浏览器默认用 application/x-www-form-urlencoded,服务端收不到 multipart.FileHeader,r.FormFile 永远返回空。
- form 标签必须显式写:
- input name 要和后端
r.FormFile("xxx")的 key 严格一致(区分大小写) - 如需多文件,用
name="files"+multiple,后端改用r.MultipartForm.File["files"]取 slice - 调试时 curl 上传:用
curl -F "file=@/path/to/a.jpg" http://localhost:8080/upload
真实项目里,文件名编码、客户端断点续传、临时目录清理、S3 代理上传这些环节才真正容易出问题,标准库只解决最基础的“收到并存下来”这一步。










