io.teereader 不能直接实现双路转发,需配合 io.multiwriter 等机制;常见错误是重复使用同一 reader,正确做法是用 multiwriter 合并多个 writer 后传入 teereader,注意错误处理、资源释放与并发安全。

io.TeeReader 不能直接实现“双路转发”
它只接受一个 io.Writer,返回一个 io.Reader;所谓“双路”,必须靠组合其他机制实现。常见错误是写两次 io.TeeReader(r, w1) 和 io.TeeReader(r, w2) —— 第二个会读不到数据,因为 r 已被第一个消耗完。
用 io.MultiWriter 实现单次写入双目标
这是最轻量、最常用的方案:把多个 io.Writer 合成一个,再喂给 io.TeeReader。
-
io.MultiWriter(w1, w2)返回的io.Writer满足接口要求,每次Write()都同步发给所有目标 - 任一
w写入失败(如磁盘满、网络断开),MultiWriter.Write()就返回该错误,进而导致TeeReader.Read()失败 - 适合日志+摘要、文件+hash 计算等场景,例如:
hasher := sha256.New() f, _ := os.Create("out.bin") mw := io.MultiWriter(f, hasher) tee := io.TeeReader(src, mw) io.Copy(io.Discard, tee) // 触发读取与双写 fmt.Printf("hash: %x", hasher.Sum(nil))
手动控制写入错误传播和资源释放
io.TeeReader 不暴露写入错误,也不关闭任何资源——你得自己兜底。
- 如果
w是os.File,记得在读完后显式调用f.Close(),否则句柄泄漏 - 若需感知写入失败(比如审计日志必须落盘),不要依赖
TeeReader自动报错;应包装w,在Write()中记录错误状态,并在主流程中检查 - 避免用
bufio.Writer包裹目标w后再传给TeeReader:缓冲会导致写入延迟,且Flush()时机不可控,可能漏写最后几字节 - 并发读取同一
tee实例时,确保所有目标w是线程安全的(bytes.Buffer可以,未加锁的os.File不行)
大文件场景下别全内存缓存
想“先存一份再分发两路”?用 bytes.Buffer 接 TeeReader 然后重读,等于把整个文件加载进内存——上传 GB 级文件时极易 OOM。
- 更稳的做法是:用
io.Pipe+ goroutine 拆流,但必须手动处理 goroutine 生命周期、错误传递和 EOF 同步 - 或者直接写临时文件:
tmp, _ := os.CreateTemp("", "tee-*.bin"),再用os.Open生成第二路 reader - 若只需其中一路做限速/进度统计,用自定义
io.Writer(仅计数不写入)配合MultiWriter即可,无需额外存储
io.TeeReader 就不是起点,而是中间一环;它的价值在于低侵入、零拷贝的旁路写入,而不是流式分发的终点。golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











