go 的 http.fileserver 仅支持 get/head 请求,无法处理文件上传;需自定义 handler,调用 r.parsemultipartform 设置 maxmemory 并用 r.formfile 读取文件,注意 name 字段严格匹配且须关闭 file。

Go 的 http.FileServer 不能直接处理上传
很多人一上来就用 http.FileServer 搭个静态服务,发现 POST 文件根本没反应——它只响应 GET/HEAD,对 multipart/form-data 请求完全无视,连 405 都不返回,直接 404。这不是配置问题,是设计如此。
真正要收文件,得自己写 handler,核心靠 r.ParseMultipartForm 和 file, header, err := r.FormFile("file")。注意字段名 "file" 必须和 HTML 表单里的 name="file" 严格一致,大小写敏感,空格也不行。
常见错误现象:
- 调用
ParseMultipartForm前没设r.MaxMemory,大文件直接 OOM - 忘记检查
err就直接取file,导致 panic: "invalid argument" - 用
io.Copy写文件后没file.Close(),后续请求读不到临时文件
上传时必须显式设置 MaxMemory 和临时目录
Go 默认把整个 multipart body 读进内存,超过 32MB 就报错 http: request body too large。这不是服务器限制,是 Go 标准库的硬编码安全策略。
解决方法是在 handler 开头加:
r.ParseMultipartForm(32 <p>但更稳妥的是提前设 <code>r.MultipartReader</code> 的底层参数:</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill6460" title="Golang Naming"><img src="https://img.php.cn/upload/skill/000/000/081/179094616043400.jpg" alt="Golang Naming" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill6460" title="Golang Naming" class="overflowclass">Golang Naming</a> <p class="overflowclass">Go(Golang)命名规范 — 包括包、构造函数、结构体、接口、常量、枚举、错误、布尔值、接收器、getter/setter、函数等。</p> </div> <a rel="nofollow" href="/xiazai/skill6460" title="Golang Naming" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div>
-
r.ParseMultipartForm(0)表示不限内存(危险,慎用) - 推荐用
r.ParseMultipartForm(10 (10MB),再配合 <code>os.TempDir()或自定义路径做磁盘缓存 - 如果想指定临时目录,需在调用前设
os.Setenv("TMPDIR", "/path/to/tmp"),否则所有FormFile返回的file都是内存-backed 的bytes.Buffer
保存文件前务必校验 header.Filename 和路径遍历风险
header.Filename 是客户端传来的原始字符串,可能含 ../../etc/passwd 这种路径穿越。Go 不会自动 sanitize,os.OpenFile 会照单全收。
正确做法是剥离路径、强制白名单扩展名、拼接绝对路径:
- 用
filepath.Base(header.Filename)取纯文件名 - 用
filepath.Ext(name)检查是否在允许列表(如[]string{".jpg", ".pdf", ".txt"}) - 最终保存路径必须用
filepath.Join(uploadDir, safeName),且uploadDir要是绝对路径(比如/var/www/uploads) - 别信
strings.HasSuffix,攻击者可以传shell.php.jpg绕过
并发上传时要注意临时文件句柄泄漏和磁盘空间耗尽
每个 r.FormFile 返回的 file 是一个 multipart.File,底层可能是 *os.File 或 *bytes.Buffer。如果没显式 file.Close(),在 Linux 上会持续占用 fd,达到 ulimit 就卡死整个服务。
另外,Go 的 multipart 临时文件默认放在 os.TempDir(),如果上传频繁且不清理,/tmp 很快爆满。建议:
- 用
defer file.Close()包裹所有FormFile使用块 - 上传成功后手动
os.Remove(file.(interface{ Name() string }).Name())(仅当它是磁盘文件时) - 或改用
io.ReadAll(file)全部读进内存再关掉,适合小文件 - 生产环境务必加磁盘空间监控,别等
no space left on device才发现
临时文件的生命周期和命名规则没人帮你管,这块最容易在线上静默出问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










