必须用io.copy流式合并,禁用os.readfile或bytes.buffer;目标文件用os.create,分片用os.open直传,按数字排序(如part_001)并校验sha256,避免内存溢出与数据错位。

分片合并必须用 io.Copy,别碰 os.ReadFile 或 bytes.Buffer
GB 级文件合并失败、PDF 打不开、ZIP 解压报 CRC 错,90% 是因为用了 os.ReadFile、io.ReadAll 或 bytes.Buffer.Write——它们会把整块数据加载进内存,一卡就 OOM。正确做法是打开目标文件后,对每个分片调用 io.Copy(dst, src),只搬运字节流,内存占用恒定在几 KB。
- 目标文件必须用
os.Create(dstPath),不是os.OpenFile(, os.O_APPEND)——后者首次创建时看似能写,但后续逻辑混乱,容易残留旧内容 - 每个分片用
os.Open(srcPath)直接传给io.Copy,中间不经过任何[]byte缓冲 -
io.Copy内部默认 32KB 缓冲区,对 NVMe/SSD 已足够;真要调优才用io.CopyBuffer包一层
分片文件名必须数字排序,不能依赖 filepath.Glob 的字典序
filepath.Glob("part_*") 返回 part_1、part_10、part_2 是正常现象,但按这顺序合并,数据必然错位。这不是 Go 的 bug,是所有字符串排序的通病。
- 生成分片时强制零填充:
fmt.Sprintf("part_%03d", i)→part_001、part_002 - 读取前必须显式提取数字再排序:
sort.Slice(files, func(i, j int) bool { return extractNum(files[i]) ,其中 <code>extractNum用strconv.Atoi(strings.TrimPrefix(filepath.Base(f), "part_")) - 若前缀复杂(如
backup_20240501_part_042),先用strings.Split或正则提取数字再转int
合并前后必须校验 sha256.Sum256,不能只比文件大小
os.Stat().Size 一致 ≠ 内容正确。末尾几个字节丢失、中间某块被截断、I/O 中断未写满——这些都可能导致大小不变但内容损坏,尤其 ZIP、视频、数据库 dump 文件会直接不可用。
- 分割前:用
sha256.New()+io.Copy流式算原始哈希,不加载全文 - 合并后:对新文件做同样哈希计算,对比两个
[32]byte值是否相等 - 校验失败必须立刻删掉目标文件和所有分片——留着脏数据比报错更危险
并发写入分片时,upload_id 和 chunk_index 必须从 URL 或 Header 取,绕过框架自动解析
Gin/Echo 默认会隐式调用 ParseMultipartForm,一旦触发,r.Body 就被读空。你写的 handler 拿不到任何二进制数据。
- 在 handler 开头第一行执行
r.ParseMultipartForm(0),强制跳过 multipart 解析 - 元数据(
upload_id、chunk_index)全部从r.URL.Query().Get("upload_id")或 header(如X-Chunk-Index)取,别碰r.FormValue - 分片 body 用
io.Copy直接写入临时文件:dst, _ := os.OpenFile(fmt.Sprintf("./uploads/%s/%d", uploadID, chunkIndex), os.O_CREATE|os.O_WRONLY|os.O_EXCL, 0644) - 写入前校验
r.ContentLength是否匹配预期大小,防前端 bug 或中间件篡改
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











