Revel框架需手动实现断点续传:前端用File API分片并携带upload_id、chunk_index等元数据;后端绕过multipart解析,直接读Body存临时文件;用Redis记录上传状态;合并前校验完整性,定时清理过期临时文件。

Revel 框架本身不内置断点续传能力,必须靠手动实现分块上传 + 状态持久化 + 客户端协同。它不像 Laravel 或 Spring Boot 那样有现成的上传中间件或 UploadSession 抽象,所有逻辑都得你亲手串起来——但好处是控制粒度细、无黑盒、适合定制安全策略。
前端切片必须用 File API + 自定义 HTTP 请求头
Revel 是纯 Go 的 MVC 框架,不处理前端,所以切片逻辑全在浏览器里。不能依赖 form 提交,必须用 File.slice() 手动分块,并为每个请求带上关键元数据:
-
upload_id(服务端生成的唯一标识,比如 UUID)必须随每个分片请求发送,不能靠 Cookie 或 Session 传递,否则并发上传时会混状态 -
chunk_index和total_chunks要作为 query 参数或 JSON body 传,避免服务端解析错误导致合并错序 - 建议加
X-Content-Range头(如bytes 0-5242879/104857600),虽然 Revel 不自动解析,但方便后续对接 Nginx 的slice模块或做日志追踪
Revel Controller 接收分片要绕过默认 multipart 解析
Revel 默认的 c.Params.Files 只适用于传统表单上传,对分块上传的 multipart/form-data 分片会失败或吃内存。正确做法是直接读原始 body:
- 在 Controller 方法里调用
c.Request.Body,用io.Copy写入临时文件,路径格式建议为./uploads/{upload_id}/part_{index} - 不要用
c.Params.Files["file"],它会触发整个 multipart 解析,大分片下容易 OOM - 务必校验
Content-Length是否匹配预期分片大小(±1KB 可接受),防止客户端伪造或网络截断
上传状态不能只靠文件系统,必须用 Redis 或 DB 记录已传索引
只靠临时目录枚举 part_* 文件来判断进度,在高并发或 NFS 共享存储下不可靠——文件可能延迟可见、rename 不原子、ls 结果不一致。Revel 项目应引入状态层:
- 用 Redis 存
upload:{upload_id}:chunks的 Set,每成功存一个分片就SADD一次chunk_index - 客户端发起续传前先 GET
/api/upload/status/{upload_id},后端查 Set 返回已传索引数组,前端据此跳过 - 避免用 MySQL 记录单个分片状态(写放大严重),但可用它存最终文件元信息(如
original_name,md5,merged_at)
合并时机和方式直接影响可靠性
合并不是“等所有分片到齐就立刻 cat”,而是要有明确触发条件和防冲突机制:
- 合并动作必须由显式请求触发(如 POST
/api/upload/merge/{upload_id}),不能监听目录变化——否则多个请求同时 merge 会导致文件损坏 - 用
os.OpenFile(..., os.O_CREATE|os.O_WRONLY|os.O_APPEND)配合io.Copy追加写,比一次性读所有分片进内存安全得多 - 合并后必须做完整性校验:比对最终文件
md5sum和前端传来的file_md5,不一致就删掉并返回错误,不能静默覆盖
最容易被忽略的是临时文件清理——Revel 没有自动 GC 机制。分片上传失败、用户放弃、合并出错都会留下孤儿文件。必须配独立的定时任务(比如 cron 每小时扫 ./uploads/* 下超过 24 小时未更新的目录并删除),否则磁盘迟早打满。











