Iris 框架需手动实现分片上传与断点续传,不可用 ctx.FormFile;须解析 multipart 请求体,按 chunkIndex、totalChunks、fileMd5 等字段处理 5MB 级分片,避免 OOM 或超时。

Iris 框架本身不内置分片上传与断点续传逻辑,必须由开发者在 HTTP 路由层手动实现分片接收、状态记录、MD5 校验、合并触发等关键环节。直接用 ctx.FormFile 接收整个文件会失败,也起不到断点效果。
分片上传必须自己解析 multipart 请求体,不能依赖 ctx.FormFile
Iris 的 ctx.FormFile 是为单次完整上传设计的,它会尝试把整个请求体读入内存或临时文件,对 GB 级分片来说极易 OOM 或超时。真实分片上传中,每个请求只含一个 chunk(比如 5MB),且携带 filename、chunkIndex、totalChunks、fileMd5 等字段。你需要:
- 用
ctx.Request().ParseMultipartForm(32 手动解析表单,避免 Iris 自动调用 <code>ParseMultipartForm时默认限制太小 - 从
ctx.Request().MultipartForm.Value里取chunkIndex、fileMd5等元数据 - 用
ctx.Request().MultipartForm.File["file"]获取文件头,再用fileHeader.Open()得到io.Reader流式写入磁盘(如os.CreateTemp) - 严禁调用
ctx.FormFile("file"),它会隐式触发完整解析,破坏分片语义
断点续传状态必须持久化,不能只存内存或临时 map
断点能力本质是“服务重启后仍能查到已传哪些块”。用内存 map 或全局变量存 map[fileMd5][]int 在多实例、滚动更新、Pod 重建场景下立即失效。正确做法是:
- 选轻量存储:SQLite(单机)、Redis(多实例共享)、PostgreSQL(带事务和并发控制)
- 建表至少包含:
file_md5 TEXT PRIMARY KEY、uploaded_chunks JSONB(或uploaded_chunks INT[])、upload_status TEXT("uploading"/"completed"/"failed") - 每次收到分片后,执行原子更新:
INSERT INTO upload_status ... ON CONFLICT(file_md5) DO UPDATE SET uploaded_chunks = array_append(...) - 检查续传时,查该
fileMd5对应的uploaded_chunks数组,返回缺失索引给前端
合并逻辑要防并发、防重复、防中间态残留
当最后一个分片到达,触发合并。但多个请求可能同时检测到 len(uploaded_chunks) == totalChunks,导致多次合并。常见陷阱包括:
- 不加分布式锁(如 Redis SETNX)就直接合并 → 多个 goroutine 同时打开同一组临时 chunk 文件 → 文件被截断或损坏
- 合并完没清空
uploaded_chunks或没设upload_status = "completed"→ 下次同 MD5 文件上传又走续传路径 - 合并过程没做原子重命名(先写
xxx.tmp,成功后再os.Rename)→ 服务中断时留下不完整文件 - 没校验最终文件 MD5 是否等于原始值 → 分片传输中出错无法发现
建议合并函数开头加 if status != "uploading" { return },结尾强制更新状态并清理临时分片目录。
前端必须主动管理分片顺序与重试,Iris 不提供自动调度
Iris 是 HTTP 路由框架,不处理浏览器端逻辑。能否断点、是否重试、按什么顺序发分片,全靠前端控制。典型配合方式是:
- 前端首次上传前,先 POST
/api/upload/check?md5=xxx,后端查 DB 返回已传 chunk 列表(如[0,1,3]) - 前端跳过已传索引,只发起
chunkIndex=2、chunkIndex=4等缺失请求 - 每个分片请求带
fetch(..., { method: "POST", body: formData }),失败时前端自行重试(最多 3 次),不要指望 Iris 重发 - 合并完成后,后端返回完整文件 URL;前端若收到 500 或超时,应再次调用
/check确认状态,而非直接报错
最易忽略的是:Iris 默认没有跨域配置,大文件上传常因 OPTIONS 预检失败静默卡住。务必在启动时加 app.UseGlobal(cors.New(cors.Config{AllowOrigins: []string{"*"}})),否则连第一个分片都发不出。


















