beego原生download不支持断点续传和流式传输,因其不解析range头、不返回206状态码及content-range头,且全量读取文件致oom;必须手动处理range请求、设置响应头并直接操作responsewriter。

Beego 原生 Download 方法不支持流式传输和断点续传,直接调用会导致大文件 OOM、无法控制响应头、浏览器强制渲染而非下载——必须绕过它,手动操作 ResponseWriter。
Beego 中 Download 方法为什么不能用于大文件或可控下载
Beego 的 Controller.Ctx.Output.Download 本质是把整个文件读进内存再 Write,它不检查 Range 头、不返回 206 Partial Content、也不设 Content-Range 或 Accept-Ranges。常见后果包括:
-
curl -r 1000-2000 -o part.bin http://x/file.zip返回200 OK而非206,断点工具直接失败 - 导出 CSV/Excel 时浏览器打开预览而不是下载,因为没设
Content-Disposition: attachment - 上传 200MB 文件时进程内存飙升甚至被 OOM Killer 杀掉,因默认
web.MaxMemory = 64
流式下载必须手动设置响应头并分块写入
要避免内存堆积、支持断点、触发浏览器下载,得放弃 Download,改用 os.Open + io.CopyN + 手动 Header 设置:
- 先调用
this.Ctx.Output.Header("Content-Type", "application/octet-stream"),避免 MIME 推断导致渲染 - 必须加
this.Ctx.Output.Header("Content-Disposition", "attachment; filename=\"report.xlsx\""),否则 iOS Safari / Chrome 可能忽略下载意图 - 对大文件,用
f, _ := os.Open(filePath)后f.Seek(offset, 0)定位,再用io.CopyN(this.Ctx.ResponseWriter, f, length)流式写出 - 若需断点续传,必须解析
this.Ctx.Request.Header.Get("Range"),计算start、end,返回状态码206和Content-Range: bytes start-end/total
上传参数不调优会导致 Download 前就 413 报错
很多人卡在“还没开始下载,上传阶段就失败”,其实是 GetFile 前的配置缺失:
-
web.MaxMemory控制单请求内存缓冲上限,默认64 (64MB),生产建议设为 <code>16 (16MB) -
web.MaxUploadSize必须 >MaxMemory,否则GetFile永远返回nil;例如设web.MaxUploadSize = 128 - 这两个值必须在
main()初始化早期设置,晚于beego.Router就无效 - 配置文件中写成
maxmemory = 16777216(字节),不是16MB
SaveToFile 路径拼接错误会让下载文件“消失”
SaveToFile 第二个参数是完整路径,不是目录,这点极易踩坑:
- 错:
f.SaveToFile("file", "./uploads/" + header.Filename)—— 若header.Filename是../../etc/passwd,就可能越权写入 - 对:先用
path.Base(header.Filename)清洗文件名,再拼路径:filepath.Join("./uploads", path.Base(header.Filename)) - 确保
./uploads/目录存在且进程有写权限,否则SaveToFile静默失败,GetFile后不检查err会导致后续file.Close()panic - 不要在
SaveToFile前做字符串替换(如删空格、去点号),path.Base已足够安全
真正难的不是写几行 io.Copy,而是记住:Beego 的下载不是“配个路由就行”的功能,它每一步都依赖显式控制——从上传参数、文件名清洗、响应头顺序,到 Range 解析逻辑,漏掉任一环,前端就表现异常,且问题往往延迟暴露。











