Beego原生不支持分片上传和断点续传,需手动解析请求、管理分片存储与状态校验;核心是禁用ParseMultipartForm,用Request.Body流式读取分片,按chunkIndex升序合并,并校验最终MD5。

Beego 原生不支持分片上传和断点续传,Controller.Ctx.Output.Download 和 Controller.Ctx.Input.ParseForm 都无法处理分片、校验、状态查询等逻辑。必须绕过封装,手动解析请求、管理分片存储、校验文件唯一性,并自行合并。核心难点不在“怎么传”,而在“怎么记”和“怎么判”——即服务端如何可靠记录每个文件的上传进度。
Beego 中如何接收并保存单个文件分片
前端按固定大小(如 2 * 1024 * 1024)切片后,每个请求携带 chunkIndex、totalChunks、fileMd5、fileName 等字段。Beego 默认会把 multipart 表单整个读入内存,大分片容易 OOM,所以必须禁用自动解析:
- 在路由注册前调用
beego.BConfig.CopyRequestBody = true(仅限旧版 Beego 1.x;新版 Beego 2+ 已移除该配置,需改用ctx.Input.RequestBody直接读流) - 在 Controller 方法里跳过
c.ParseForm(),改用c.Ctx.Request.ParseMultipartForm(32 并限制最大内存为 32MB - 用
c.Ctx.Request.MultipartForm.File["file"]获取文件句柄,再通过fileHeader.Open()得到io.ReadCloser - 分片文件建议存为:
/uploads/{fileMd5}/{chunkIndex},避免文件名冲突,也便于后续校验和合并
如何实现“断点续传”的状态查询接口
客户端在上传前必须先发起一次 GET 请求(如 /api/upload/status?md5=xxx&name=xxx),服务端需返回已上传的分片索引列表。这个接口不能查数据库再拼数组——高并发下慢且易错,推荐直接扫描目录:
- 用
os.ReadDir(fmt.Sprintf("/uploads/%s", md5))列出所有分片文件名 - 过滤出纯数字命名的文件(即
chunkIndex),转成int后存入 map 或 slice - 响应 JSON 示例:
{"uploaded": true, "uploadedChunks": [0,1,3,4], "totalChunks": 5} - 注意加缓存:对同一
md5的查询结果可缓存 5 分钟,用beego.Cache或本地 map + 时间戳控制 - 若目录不存在或为空,返回
"uploaded": false,前端就从 0 开始传
合并分片时容易忽略的三个细节
所有分片上传完成后,前端调用合并接口(如 POST /api/upload/merge),服务端执行合并。这不是简单地 cat a b c > final,要注意:
- 必须按
chunkIndex升序打开文件,否则合并后文件内容错乱;可用sort.Ints(chunkIndices)排序后再遍历 - 合并目标文件应先创建空文件(
os.Create(finalPath)),再用os.O_APPEND模式逐个写入,避免内存爆满;不要用ioutil.WriteFile一次性加载全部分片 - 合并完成后务必校验最终文件的 MD5 是否与原始值一致,不一致则删除目标文件并返回错误;校验失败常见于分片丢失、网络粘包或前端计算 MD5 时未按字节流顺序读取
最常被跳过的一步是:没有为每个分片生成独立的临时路径并设置清理机制。上传中断后残留的碎片文件会越积越多,需要配合定时任务(如每小时扫一次 /uploads/* 下 24 小时未更新的目录并删除)或在合并成功后主动清理源分片目录。否则磁盘空间和 inode 都会悄悄耗尽。


















