大文件上传下载需显式配置内存、超时和流式行为;e.Static()因不支持Range请求且可能全量读入内存而卡住,应改用c.File()或e.File();上传前须设BodyLimit和ParseMultipartForm。

大文件上传下载在 Echo 中不能靠默认配置硬扛,必须显式控制内存、超时和流式行为;否则 50MB+ 视频大概率上传失败或下载中断。
为什么 e.Static() 会卡住大视频下载
因为 e.Static() 底层用的是 http.FileServer 的简单封装,不调用 http.ServeContent,导致:它不会自动处理 Range 请求、不设 Accept-Ranges: bytes、不校验 If-Modified-Since,更关键的是——它可能把整个文件读进内存再发,触发 Go HTTP Server 默认的 30 秒 WriteTimeout,或被 Nginx/Cloudflare 中间层断连。现象就是浏览器进度条停住、报 net::ERR_INCOMPLETE_CHUNKED_ENCODING 或直接 ERR_CONNECTION_RESET。
解决办法只有一个:弃用 e.Static(),改用 c.File()(动态路径)或 e.File()(固定路径),它们内部调用 http.ServeContent,走 io.Copy 流式转发,内存占用恒定,且天然支持断点续传和拖拽播放。
上传大文件前必须配 BodyLimit 和 MultipartForm
默认情况下,Echo 对请求体大小无限制,但实际会被 Go net/http 的底层缓冲或代理截断;上传大视频时,c.FormFile() 会先将整个 multipart 数据载入内存解析,不设限极易 OOM。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 用
middleware.BodyLimit("100MB")全局限制请求体上限(注意单位是字符串,如"50MB") - 调用
c.MultipartForm()前,务必先执行c.Request().ParseMultipartForm(32 (即 32MB 内存缓冲),否则大文件表单解析会 panic - 上传路径要校验:检查
file.Filename是否含".."或以"."开头,防止路径遍历读取系统文件
c.File() 下载时的安全与 MIME 控制
c.File(filepath) 虽方便,但有三个隐藏风险点:
- 路径未校验 → 可能返回
/etc/passwd等敏感文件;必须手动过滤filename参数,建议用filepath.Clean()+ 白名单目录比对 - MIME 类型依赖扩展名 → 若文件无后缀或后缀错误(如
video.bin),c.File()会设成application/octet-stream,浏览器无法内联播放;可手动调用c.Attachment(filepath, filename)或先用mime.TypeByExtension()推断并显式c.Header().Set("Content-Type", ...) - 没设超时 → 大文件传输若卡在网络慢链路上,连接可能被中间设备静默关闭;需同步调整 Echo Server 的
ReadTimeout和WriteTimeout(至少设为 300 秒)
上传后不要用 ioutil.ReadAll 读完整文件
很多示例代码里写 buf := make([]byte, file.Size); src.Read(buf) ——这等于把整个视频加载进内存,50MB 就占 50MB 堆空间,高并发下直接崩溃。
正确做法永远是流式处理:
- 保存:用
c.SaveFile(file, dstPath)(内部用io.Copy) - 转存到对象存储(如 S3):用
io.Copy(s3Uploader, src),不落地 - 校验(如计算 MD5):边读边哈希,
hash.Hash.Write()接io.TeeReader(src, hash)
真正容易被忽略的是:c.FormFile() 返回的 *multipart.FileHeader 里 Size 字段是客户端声明的大小,不可信;实际读取长度应以 io.Copy 返回值为准,否则可能因前端伪造 size 导致逻辑错乱。

















