分片上传必须避免os.readfile或io.readall全量加载,应使用os.open配合io.readat按偏移读取固定块(如5mb),每次仅载入一块;服务端需校验分片完整性、严格按序合并并验证最终哈希。

分片上传时怎么避免内存爆掉
直接 os.ReadFile 或 io.ReadAll 大文件,1GB 就吃掉 1GB 内存,OOM 是分分钟的事。关键不是“能不能读”,而是“要不要全读”。
- 用
os.Open打开文件,配合io.ReadAt按偏移量读取固定大小块(如 5MB),每次只加载一块到内存 - 别用
bufio.Reader包裹后再调ReadAt——它不支持随机读,offset 会错乱 - 最后一块长度不足时,
ReadAt返回的n一定小于请求长度,必须用buf[:n]截断,否则可能带脏数据 - 如果中间要算哈希或加密,用流式方式:
hash := sha256.New(); io.Copy(hash, filePieceReader),别先读全再算
服务端如何安全暂存并合并分片
分片不是存完就完事,写错位置、顺序错乱、缺片不报错,最后合并出一个打不开的文件才是真崩溃。
- 每个分片存为独立文件,命名必须零填充(如
chunk_001、chunk_002),否则filepath.Glob("chunk_*")排序会变成chunk_1→chunk_10→chunk_2 - 合并前先检查是否存在全部
0..N-1的分片文件,缺任一片直接返回400 Bad Request,别硬凑 - 用
os.Create创建目标文件(不是os.OpenFile(..., os.O_APPEND)),然后按序号从小到大io.Copy每个分片 - 合并完成后立刻用
sha256.New()+io.Copy计算最终文件哈希,和客户端传来的X-Final-SHA256对比;不一致就删掉整个结果文件和所有分片
断点续传靠什么判断“缺哪几片”
不是靠客户端记日志,也不是靠服务端猜——双方必须有一份轻量、可快速比对的状态视图。
- 客户端上传前先发
GET /api/v1/upload/status?file_id=xxx,服务端返回已上传的offset列表(如[0, 2, 3, 5]) - 服务端状态建议存在内存
map[string]map[int]bool或 Redis 中,键是file_id,值是已收分片索引集合 - 客户端只传缺失的片,且每片带上确定性生成的
chunk_id(如sha256(file_path + strconv.Itoa(index))[:8]),避免 UUID 导致服务端无法去重 - 上传失败时,服务端不删已存分片,客户端下次从失败 offset 继续,而不是重头开始
并发上传怎么控制才不打爆连接池
开 100 个 goroutine 同时传 100 片?网络抖动一来,超时、重试、TIME_WAIT 堆满,连接池先崩。
- 用
sync.WaitGroup控制生命周期,别用 channel 分发任务——简单分片场景下,channel 是过度设计,还容易死锁 - 并发数参考
runtime.NumCPU(),上限设为 3–5 片同时上传,实测比盲目拉高更稳 - 每片上传设 30s 超时,失败自动重试 2 次,超过则中断并返回明确错误,别让 goroutine 卡住
- HTTP 客户端务必设置
http.Transport.MaxIdleConnsPerHost(如 20),防止默认值(2)在并发下成为瓶颈
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











