讲师中心 微信公众号
AI工具推荐 视频效率加速

使用Golang IO包的TeeReader监控读取进度_流量统计实现

阿静酱_2382

阿静酱_2382

发布时间:2026-02-24 15:23:02

|

554人浏览过

|

来源于php中文网

原创

TeeReader 不能直接统计读取字节数,因其设计仅转发数据且不记录累计量;必须通过自定义 io.Writer(如 counterWriter)配合 MultiWriter 或封装 ProgressReader 才能准确计数。

使用golang io包的teereader监控读取进度_流量统计实现

为什么 TeeReader 不能直接用来统计读取字节数

TeeReader 的设计目标是「把读到的数据同时写进另一个 Writer」,它本身不记录、不暴露已读字节数。你调用 Read() 时,它只返回本次读取的 n,不会累加或上报总进度。常见错误是以为包装后就能直接查「当前读了多少」,结果发现没接口、没字段、也没回调。

真正能拿到进度的方式,得靠你自己在 Writer 侧做计量——也就是把 TeeReader 的「副输出」做成一个计数器。

  • 必须自己实现一个满足 io.Writer 接口的类型,内部维护 int64 计数器
  • 这个 Writer 的 Write() 方法只负责累加长度,返回 len(p)(不能返回 0 或错误,否则 TeeReader 会中断)
  • 不要试图从 TeeReader 本身取值,它没有公开字段或方法暴露累计量

用 io.MultiWriter + 自定义计数 Writer 实现可靠流量统计

单独用 TeeReader 太单薄,实际场景往往还要把数据转发给别的地方(比如日志、缓存、加密器)。这时候推荐组合 io.MultiWriter 和自定义计数器,既清晰又可复用。

示例中 counterWriter 只做一件事:每次 Write() 就把长度加到 total 字段里。把它和真实目标 dst 一起塞进 MultiWriter,再喂给 TeeReader:

Golang Spf13 Viper
Golang Spf13 Viper

Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。

下载

立即学习“go语言免费学习笔记(深入)”;

type counterWriter struct {
	total int64
}

func (c *counterWriter) Write(p []byte) (n int, err error) {
	return len(p), nil // 忽略写入失败,只计数
}

func (c *counterWriter) Total() int64 { return c.total }

// 使用:
cw := &counterWriter{}
mr := io.MultiWriter(cw, dst) // dst 是你真正的写入目标(如文件、网络连接)
tr := io.TeeReader(src, mr)

// 后续读取 tr,cw.Total() 就是已读总字节数
  • 注意 Write() 必须返回 len(p),返回其他值会导致 TeeReader 提前退出或 panic
  • 如果 dst 是可能出错的 Writer(如网络连接),MultiWriter 会传播第一个错误;但计数器不能因此失败,所以它的 Write() 要忽略错误
  • 并发读取时,total 字段需加 sync/atomic 保护,否则读写竞争会导致统计不准

TeeReader 在 HTTP 上传/下载中监控进度的典型陷阱

HTTP 场景下,TeeReader 常被用在 http.Request.Body 或 http.Response.Body 上。但这里有几个硬限制容易踩坑:

  • Request.Body 默认是不可重读的,一旦被 TeeReader 包装并读过一次,后续中间件或 handler 再读就会得到空内容——除非你提前用 httputil.DumpRequest 或 io.Copy(ioutil.Discard, ...) 把原始 body 缓存下来
  • Response.Body 如果来自 http.Transport,底层可能是带缓冲的连接;强制用 TeeReader 中间截流可能导致连接复用失效,甚至触发服务端超时(尤其大文件下载)
  • 别在 http.HandlerFunc 里直接对 r.Body 做 io.TeeReader 包装后传给下游库(如 json.NewDecoder),因为下游可能调用 Read() 多次,而你的计数器只会按「每次写入长度」累加,不是按「逻辑单元」(比如一个 JSON 对象)

替代方案:什么时候该放弃 TeeReader 改用 io.ReadCloser 包装

如果你的真实需求不只是「边读边记数」,而是要「控制读取节奏」「支持取消」「需要精确到 chunk 级别的回调」,那 TeeReader 就太轻了。它只是一个被动管道,没法拦截、没法暂停、也没法注入上下文。

更可控的做法是自己封装一个 io.ReadCloser,在 Read() 里手动调用源 Read(),然后更新计数、触发回调、检查 ctx.Done():

type ProgressReader struct {
	r     io.Reader
	total *int64
	mu    sync.Mutex
}

func (pr *ProgressReader) Read(p []byte) (n int, err error) {
	n, err = pr.r.Read(p)
	pr.mu.Lock()
	*pr.total += int64(n)
	pr.mu.Unlock()
	return n, err
}
  • 这种写法比 TeeReader 多几行代码,但你能完全掌控读行为,比如在读满 1MB 后 sleep 10ms 控制速率
  • 如果上游 Reader 本身不支持 io.Seeker(比如管道、网络流),就别幻想「回退」或「跳转」,所有进度都只能是单向累计
  • 最易被忽略的一点:TeeReader 的副写入路径(Writer)如果阻塞,整个 Read() 就会卡住——而自定义 ReadCloser 可以把计数逻辑做成非阻塞(比如用 channel 异步上报)

热门AI工具

更多
咔片AIPPT

一款在线AI演示文稿制作工具,可根据主题和内容需求辅助生成PPT结构与页面,提高演示材料制作效率。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

LibLibAI
LibLibAI Hot

一款AI视频创作工具,主要用于国内领先的AI创意平台,以海量模型、低门槛操作与“创作-分享-商业化”生态,让小白与专业创作者都能高效实现图文乃至视频创意表达,适合需要提升相关任务效率的用户。

讯飞智作

讯飞智作是一款AI视频创作工具,AI文本配音工具,数字人课程、营销视频制作。

SkildArt
SkildArt Hot

SkildArt是一款AI文本写作工具,一站式 AI 视觉创作平台。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

相关专题

更多
golang如何定义变量
golang如何定义变量

golang定义变量的方法:1、声明变量并赋予初始值“var age int =值”;2、声明变量但不赋初始值“var age int”;3、使用短变量声明“age :=值”等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

499

2024.02.23

golang有哪些数据转换方法
golang有哪些数据转换方法

golang数据转换方法:1、类型转换操作符;2、类型断言;3、字符串和数字之间的转换;4、JSON序列化和反序列化;5、使用标准库进行数据转换;6、使用第三方库进行数据转换;7、自定义数据转换函数。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

576

2024.02.23

golang常用库有哪些
golang常用库有哪些

golang常用库有:1、标准库;2、字符串处理库;3、网络库;4、加密库;5、压缩库;6、xml和json解析库;7、日期和时间库;8、数据库操作库;9、文件操作库;10、图像处理库。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1016

2024.02.23

golang和python的区别是什么
golang和python的区别是什么

golang和python的区别是:1、golang是一种编译型语言,而python是一种解释型语言;2、golang天生支持并发编程,而python对并发与并行的支持相对较弱等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

771

2024.03.05

golang是免费的吗
golang是免费的吗

golang是免费的。golang是google开发的一种静态强类型、编译型、并发型,并具有垃圾回收功能的开源编程语言,采用bsd开源协议。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1486

2024.05.21

golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

4134

2025.06.09

golang相关判断方法
golang相关判断方法

本专题整合了golang相关判断方法,想了解更详细的相关内容,请阅读下面的文章。

1854

2025.06.10

golang数组使用方法
golang数组使用方法

本专题整合了golang数组用法,想了解更多的相关内容,请阅读专题下面的文章。

3806

2025.06.17

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

0

2026.09.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn