不能用 os.ReadFile 处理大文件,因其会一次性加载全部内容到内存导致 OOM;必须流式分片读取,使用 io.ReadAt 或 Seek+ReadFull,避免并发读错位、缓冲区不当、拼接 OOM、追加写混乱、分片排序错误及校验缺失。

为什么不能用 os.ReadFile 处理大文件
它会一次性把整个文件加载进内存,读 2GB 文件就占 2GB RAM,进程大概率被系统 OOM killer 终止。并发调用时问题指数级放大,不是“慢”,是直接崩溃。日志解析、视频切片、数据库 dump 这类场景,必须流式处理。
分片读取必须用 io.ReadAt 或 file.Seek + io.ReadFull
同一个 *os.File 不支持并发 Read() —— 它共享内部 offset,多个 goroutine 读会错位、跳段、返回空数据。正确做法是每个分片独立打开文件、定位偏移、读满指定长度:
-
io.ReadAt(buf, offset)线程安全,适合随机读,但需手动计算剩余长度(最后一块可能不足) - 更稳的是
file.Seek(offset, io.SeekStart)后调io.ReadFull(f, buf),强制读满,避免短读导致解析错位 - 缓冲区大小建议 64KB–1MB;太小 syscall 过多,太大失去流式意义
- 别用
bufio.Scanner分片读二进制——它按行切割,遇到\x00或超长无换行数据会 panic
合并必须用 io.Copy,且目标文件要用 os.Create
任何把分片内容先读进 []byte、bytes.Buffer 或 strings.Builder 再拼接的做法,都会在 GB 级文件上 OOM。合并的本质是字节流搬运,不是字符串操作:
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
- 目标文件必须用
os.Create(dstPath)—— 自动清空并覆盖,不是os.OpenFile(..., os.O_APPEND),后者首次创建行为等价于普通写,后续追加逻辑混乱 - 分片文件名必须零填充排序:
part_001、part_002,否则filepath.Glob("part_*")返回的顺序是字典序(part_1、part_10、part_2),合并后数据错位 - 排序不能依赖
os.ReadDir返回顺序,必须提取数字后显式排序:sort.Slice(files, func(i, j int) bool { return extractNum(files[i]) < extractNum(files[j]) })
校验完整性不能只比文件大小,必须用 sha256.Sum256
大小一致 ≠ 内容正确。末尾字节丢失、某块写入不全、I/O 中断未 flush —— 这些都可能导致文件损坏但 size 不变。ZIP 打不开、视频花屏、数据库 dump 导入失败,90% 出在这一步漏校验:
立即学习“go语言免费学习笔记(深入)”;
- 分割前:用
sha256.New()+io.Copy流式算原始哈希 - 合并后:对新文件做同样哈希计算,对比两个
[32]byte值是否相等 - 校验失败必须立刻删掉目标文件和所有分片 —— 留着脏数据比报错更危险
- 别用
md5,碰撞风险高;也别跳过校验,写对了却没验证,等于没写

















