Iris MVC 不内置分片上传逻辑,须手动实现裸路由处理;Controller 实例每次新建无法持状态,ctx.Request().Body 为一次性流,超时与反代配置亦需同步调优。

Iris MVC 本身不内置分片上传或断点续传逻辑,必须手动实现;直接在 Controller 方法里读 ctx.Request().Body 容易阻塞、OOM 或丢失分片上下文,这是最常踩的坑。
为什么不能直接用普通 handler 方式写上传逻辑
在 Iris MVC 中,若把分片上传写成 func (c *UploadCtrl) PostChunk(ctx iris.Context) 这种形式,看似自然,但实际会遇到三类硬伤:
- Controller 实例每次请求新建,无法天然持有分片状态(如已接收哪些 chunk、临时文件句柄、MD5 累计值)
-
ctx.Request().Body是一次性流,没做缓冲就直接读会导致后续中间件或日志拿不到原始 body,Iris 的ctx.FormFile对多 part 表单友好,但对 raw binary 分片不自动识别边界 - 大文件上传时长超过默认 HTTP 超时(Iris 默认 30s),未显式调大
app.ConfigureHost中的ReadTimeout和WriteTimeout,连接会被静默中断
推荐方案:分离上传控制流与业务逻辑
把“分片接收”和“合并校验”拆开,前者用轻量路由兜底,后者进 MVC 做协调。关键不是“能不能在 Controller 里写 upload”,而是“该不该让 Controller 承担状态管理”。
- 用
app.Post("/upload/chunk", chunkHandler)定义裸路由处理分片,不走 MVC,避免实例生命周期干扰 - chunkHandler 内使用内存映射或临时目录 + 原子写入保存每个分片,文件名建议含
upload_id+chunk_index,例如/tmp/uploads/abc123_0005.bin - 用 Redis 或本地 BoltDB 记录每个
upload_id的已收分片索引集合、总大小、原始文件名,供后续合并校验用 - 合并触发走 MVC:定义
func (c *UploadCtrl) PostMerge(ctx iris.Context),只负责查 Redis、校验完整性、调os.Rename或io.Copy合并,不碰原始 body 流
如何安全读取原始分片数据
分片请求通常是 Content-Type: application/octet-stream,且无表单边界。Iris 不像 Gin 那样有 c.GetRawData(),必须自己控制读取流程:
- 禁用 Iris 自动解析 body:
app.UseRouter(func(ctx iris.Context) { ctx.Next() })避免提前消费Body - 在裸 handler 中,用
io.Copy直接写入临时文件,而非ctx.ReadBody()—— 后者会把整个分片加载进内存 - 务必设置
ctx.Request().Body读取上限:ctx.Request().Body = http.MaxBytesReader(ctx.ResponseWriter(), ctx.Request().Body, 10*1024*1024)(例如限制单个分片 ≤10MB) - 读完后显式
ctx.Request().Body.Close(),否则底层 TCP 连接可能无法释放
容易被忽略的细节
真正上线时,最麻烦的往往不是代码逻辑,而是环境协同:
- Nginx 反向代理需同步调大
client_max_body_size、client_body_timeout、proxy_read_timeout,否则请求会在网关层被截断,Iris 根本收不到 - Linux 系统默认
fs.file-max和进程级ulimit -n可能不够,大量并发分片上传会报too many open files - 合并阶段若用
os.Create+ 多次io.Copy,注意文件权限是否继承自临时目录(如/tmp默认 1777),生产环境应os.Chmod显式设为 0644 - 前端传的
upload_id必须全局唯一且防碰撞,别用uuid.New()就完事——要结合用户 ID + 时间戳 + 随机盐再哈希,否则恶意用户可枚举upload_id删除他人分片


















