必须手动接管request.body,用progressreader包装并解析multipart.reader,配合客户端轮询或sse/ws推送进度;禁用c.formfile()等自动解析方法。

上传时如何获取实时进度(Go + Echo)
Echo 本身不提供上传进度监听能力,echo.Context.FormFile 是阻塞式读取,整个文件必须先落地到内存或临时磁盘后才返回。想“边传边报进度”,得绕过 Echo 默认的表单解析流程,自己接管 http.Request.Body 的流式读取。
常见错误是试图在 c.FormFile("file") 调用前插进度逻辑——此时请求体可能已被部分消费,后续解析会失败或丢数据。
- 必须禁用 Echo 自动解析:在路由注册时加
e.Use(middleware.BodyDump())无用,真正要的是跳过ParseMultipartForm,即不要调用任何c.Form*方法 - 用
r.MultipartReader()获取原始multipart.Reader,再逐 part 解析,遇到Content-Disposition: form-data; name="file"时包装其io.Reader为带进度回调的版本 - 注意
boundary从Content-Type头中提取,别硬编码;Echo 的c.Request().Header.Get("Content-Type")可能含空格和参数,需用mime.ParseMediaType提取
如何构造可上报进度的 io.Reader(带回调)
核心是写一个包装器,把原始 io.Reader(比如 multipart 的 file part body)套一层,在每次 Read(p []byte) 后触发回调,把已读字节数发给前端(如通过 WebSocket、SSE 或临时存储供轮询)。
不要用全局变量存进度,多个并发上传会冲突;建议绑定到当前请求上下文,例如用 c.Set("upload_progress", &progress{...}),并在 handler 中启动 goroutine 定期写入 Redis 或内存 map(key 为 request ID)。
Echo框架 5.1.0 版本源码包下载,适合关注 RealIP 行为变化、StartConfig.Listener、NewDefaultFS 和观测性中间件入口的开发团队。
- 回调函数里避免阻塞:上传大文件时频繁写 DB 或发 HTTP 请求会导致卡顿,推荐写入本地
sync.Map或 Redis 的SET+ 过期时间 - 进度值建议同时存
uploaded和total,前端才能算百分比;total在解析 multipart header 时就能从Content-Length字段拿到(不是请求头的 Content-Length,是该 part 的Content-Length,但实际 multipart 标准不强制带,更可靠的是靠客户端在 FormData 附带 hidden 字段传 total size) - 示例包装器关键片段:
type ProgressReader struct { io.Reader callback func(n int) } func (pr *ProgressReader) Read(p []byte) (int, error) { n, err := pr.Reader.Read(p) pr.callback(n) return n, err }
前端如何配合实现“上传中状态返回”
前端不能只发个 fetch 然后等响应——那样只能拿到最终结果。要实时状态,必须用流式通信方式:
- 推荐 SSE(Server-Sent Events):后端用
c.Response().Header().Set("Content-Type", "text/event-stream"),保持连接,持续写data: {...}\n\n;前端用EventSource监听 - 如果已有 WebSocket 连接,直接复用更稳定;但注意 Echo 默认不内置 WS 支持,需额外集成
gorilla/websocket并手动升级连接 - 轮询(Polling)最简单但不推荐:每秒 GET
/upload/status?id=xxx,容易压垮服务;若必须用,务必加缓存层(如用time.Now().UnixMilli()做 key 存 Redis,TTL 设为 5 秒) - 上传开始前,前端应生成唯一
upload_id,随表单一起提交(作为 hidden 字段),后端用它作进度存储 key,避免不同用户间混淆
为什么不用第三方中间件(如 echo-multipart-progress)
市面上少数几个标榜“Echo 上传进度”的库,实际仍是基于定时轮询临时文件大小,或依赖 Nginx/Apache 的 upload module —— 这些方案与 Go 应用层无关,且强耦合反向代理配置。
真正由 Echo 进程内控制的流式进度,必须自己处理 MultipartReader。所谓“中间件”往往只是帮你注册了路由和基础结构,核心读取逻辑仍需手写。
容易被忽略的一点:HTTP/2 下,客户端可能分帧发送,而 MultipartReader 仍能正常工作;但某些旧版 iOS WebView 或低版本安卓浏览器对 SSE 支持差,这时 fallback 到 iframe + form target 模拟异步,就无法获知实时进度——得接受“仅支持现代浏览器”这个事实。










