不会——http.servefile默认content-disposition为inline,浏览器可能直接打开而非下载;需手动设置attachment头,且必须校验路径防遍历,推荐用servecontent替代以支持断点续传和缓存控制。

用 http.ServeFile 快速提供静态文件下载
直接暴露文件路径给客户端下载,最简方式就是 http.ServeFile。它会自动设置 Content-Disposition: attachment 吗?不会——默认是 inline,浏览器可能直接打开而非下载。
实操建议:
- 若想强制下载,得手动设置响应头:
w.Header().Set("Content-Disposition", "attachment; filename=\""+filepath.Base(path)+"\"") -
http.ServeFile不校验路径遍历(如../../etc/passwd),必须先用filepath.Clean并检查是否仍在允许目录内 - 注意:传入的
path是磁盘绝对路径;若用相对路径,服务启动目录会影响结果,容易 404
用 io.Copy + os.Open 精确控制下载行为
比 http.ServeFile 更可控,适合需要鉴权、限速、记录日志或动态生成文件名的场景。
常见错误现象:文件下载后打不开、大小为 0、中文名乱码
实操建议:
- 务必在
os.Open后检查err,否则空文件句柄传给io.Copy会导致静默失败 - 设置
Content-Type:用mime.TypeByExtension(filepath.Ext(name)),未知类型 fallback 到application/octet-stream - 文件名含中文时,
Content-Disposition要用 RFC 5987 编码(不是 URL 编码),例如:filename*=UTF-8''%E6%96%87%E4%BB%B6.pdf - 大文件别用
ioutil.ReadAll全部读进内存,io.Copy流式转发更安全
处理大文件下载时的超时与连接中断
默认 http.Server 的 ReadTimeout 和 WriteTimeout 对长连接下载不友好,容易中途断开。
使用场景:GB 级日志包、数据库备份导出
实操建议:
- 关闭
WriteTimeout(设为 0),但保留ReadTimeout防止慢速攻击 - 用
ResponseWriter的Hijack或Flush主动推送数据块,避免缓冲区堆积导致延迟感知异常 - 客户端断连后,
io.Copy会返回write: broken pipe或use of closed network connection,应捕获并清理资源(如file.Close()) - 考虑加
Range请求支持(HTTP 断点续传),需解析req.Header.Get("Range")并返回206 Partial Content
为什么 http.ServeContent 比裸写 io.Copy 更值得用
它内置了 ETag、Last-Modified、Range、If-None-Match 等逻辑,不是“多此一举”,而是避免重复造轮子翻车。
参数差异关键点:
- 必须传入
modtime(通常用fi.ModTime()),否则If-Modified-Since判定失效 -
contentLength不能传 -1,即使文件大小未知——得先Stat获取Size() -
serveContent内部调用io.Copy,但会在开头检查客户端是否已缓存,命中则直接返回 304,省带宽也省磁盘 I/O - 注意:它不会自动加
Content-Disposition,仍需手动设置才能触发下载而非预览
真正难的不是写出能下载的代码,而是让下载在 Nginx 反代、CDN 缓存、移动端弱网、浏览器并发限制下依然稳定可预期——这些细节藏在响应头、超时配置和错误恢复里,而不是第一行 func handler(w http.ResponseWriter, r *http.Request) 里。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











