
本教程详细介绍了如何在golang中实现文件下载或数据流处理时的实时字节计数与进度监控。通过创建一个自定义的io.reader包装器,我们可以在数据传输过程中拦截并记录每次读取的字节数,从而实时显示下载进度,而非仅在操作完成后获取最终结果。
在Go语言中,io.Reader和io.Writer是核心的I/O抽象接口,它们分别定义了Read和Write方法。这种接口设计使得Go的I/O操作具有高度的灵活性和可组合性。当我们需要在数据流传输过程中执行额外操作(例如计算已传输字节数、校验数据或进行数据转换)时,Go的组合式设计模式便能派上用场。我们可以创建一个结构体,它嵌入(或包含)一个现有的io.Reader或io.Writer,并实现自己的Read或Write方法,在这个方法中,我们先执行自定义逻辑,然后将调用转发给底层的Reader/Writer。
为了实时监控文件下载的字节数,我们将创建一个名为ProgressReader的自定义io.Reader。这个结构体将包装原始的数据源(例如HTTP响应体),并在每次读取操作时更新已传输的总字节数,并可选地打印当前进度。
首先,定义ProgressReader结构体及其Read方法:
LongShot
LongShot 是一款 AI 写作助手,可帮助您生成针对搜索引擎优化的内容博客。
77
查看详情
package main
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"strings"
"time" // 用于控制打印频率
)
// ProgressReader 包装一个 io.Reader,用于跟踪读取的字节数。
type ProgressReader struct {
io.Reader
totalRead int64 // 已读取的总字节数
name string // 阅读器名称,用于打印区分
totalSize int64 // 总文件大小 (可选,用于计算百分比)
lastPrint time.Time // 上次打印时间,用于控制打印频率
}
// NewProgressReader 创建一个新的 ProgressReader 实例。
func NewProgressReader(reader io.Reader, name string, totalSize int64) *ProgressReader {
return &ProgressReader{
Reader: reader,
name: name,
totalSize: totalSize,
lastPrint: time.Now(),
}
}
// Read 方法 '重写' 了底层 io.Reader 的 Read 方法。
// io.Copy 会调用此方法,我们在这里跟踪字节计数并转发调用。
func (pr *ProgressReader) Read(p []byte) (int, error) {
n, err := pr.Reader.Read(p)
pr.totalRead += int64(n)
// 控制打印频率,避免频繁输出导致性能问题或日志过多
// 或者在文件末尾 (io.EOF) 时强制打印最后一次进度
if time.Since(pr.lastPrint) > 100*time.Millisecond || err == io.EOF {
if pr.totalSize > 0 {
progress := float64(pr.totalRead) / float64(pr.totalSize) * 100
fmt.Printf("\r[%s] 已下载: %d / %d 字节 (%.2f%%)", pr.name, pr.totalRead, pr.totalSize, progress)
} else {
fmt.Printf("\r[%s] 已下载: %d 字节", pr.name, pr.totalRead)
}
pr.lastPrint = time.Now()
}
// 下载完成时换行,使最终的完成信息显示在新行
if err == io.EOF {
fmt.Println()
}
return n, err
}
func main() {
// 示例1: 使用内存数据模拟进度跟踪
fmt.Println("--- 示例1: 内存数据进度跟踪 ---")
var src io.Reader
var dst bytes.Buffer
// 创建一些随机输入数据作为源
testData := strings.Repeat("Some random input data for testing progress. ", 100)
src = bytes.NewBufferString(testData)
dataSize := int64(len(testData))
// 使用我们的自定义 ProgressReader 包装源 Reader
progressSrc := NewProgressReader(src, "内存源", dataSize)
count, err := io.Copy(&dst, progressSrc)
if err != nil {
fmt.Println("复制内存数据时出错:", err)
os.Exit(1)
}
fmt.Printf("内存数据传输完成,总计 %d 字节\n\n", count)
// 示例2: 应用于实际文件下载
fmt.Println("--- 示例2: 实际文件下载进度跟踪 ---")
// 替换为实际可用的下载链接,例如一个测试文件。
// 注意:某些链接可能需要处理重定向或SSL证书问题。
downloadURL := "https://speed.hetzner.de/100MB.bin" // 一个100MB的测试文件
outputFileName := "downloaded_file.bin"
fmt.Printf("开始下载文件: %s\n", downloadURL)
// 1. 发起HTTP GET请求
resp, err := http.Get(downloadURL)
if err != nil {
fmt.Println("发起HTTP请求失败:", err)
return
}
defer resp.Body.Close() // 确保关闭响应体
if resp.StatusCode != http.StatusOK {
fmt.Printf("下载失败,HTTP状态码: %d\n", resp.StatusCode)
return
}
// 2. 获取文件总大小 (如果可用)
var totalSize int64
if resp.ContentLength > 0 {
totalSize = resp.ContentLength
fmt.Printf("文件总大小: %d 字节\n", totalSize)
} else {
fmt.Println("无法获取文件总大小,将显示已下载字节数。")
}
// 3. 创建输出文件
outFile, err := os.Create(outputFileName)
if err != nil {
fmt.Println("创建输出文件失败:", err)
return
}
defer outFile.Close() // 确保关闭文件
// 4. 使用 ProgressReader 包装响应体
progressReader := NewProgressReader(resp.Body, "下载器", totalSize)
// 5. 将数据从 ProgressReader 复制到输出文件
downloadedBytes, err := io.Copy(outFile, progressReader)
if err != nil {
fmt.Println("下载文件时出错:", err)
return
}
fmt.Printf("文件下载完成,总计 %d 字节保存到 %s\n", downloadedBytes, outputFileName)
}在上面的代码中:
要将此ProgressReader应用于实际的文件下载,只需将http.Get返回的resp.Body(它是一个io.ReadCloser,因此也是io.Reader)包装起来即可。上述main函数中的“示例2: 实际文件下载进度跟踪”部分已经展示了这一用法:
以上就是Golang中实现文件下载实时进度监控:自定义io.Reader的实践的详细内容,更多请关注其它相关文章!
# 可选
# 合肥seo外包价格
# 南通网站建设入门
# 旅游互联网营销推广方向
# seo 01视频
# 贵州网站建设的定位
# 南翔营销推广哪家服务好
# 深圳租赁大楼网站建设
# 酒店seo案例
# 宝鸡网站建设与制作公司
# 丽水关键词排名提升软件
# 在这里
# 这一
# 自己的
# go
# 我们可以
# 过程中
# 器中
# 应用于
# 创建一个
# 自定义
# 状态码
# ai
# ssl
# 字节
# go语言
# golang
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
《爱笔思画x》涂色教程
狙击外星人小游戏在线链接_狙击外星人小游戏网页链接
《i莞家》修改昵称方法
Sublime怎么自动添加CSS前缀_Sublime安装Autoprefixer插件
PHP与SQL实践:高效实现数据复制与特定列值修改
windows server2019显卡驱动怎么安装_winserver2019显卡驱动安装与远程桌面优化
如何在CSS中使用伪类选择器_hover实现悬停效果
iPhone 13 mini如何清理Safari缓存_iPhone 13 mini浏览器缓存清理方法
J*aScript:从子元素中批量移除特定CSS类
Linux如何自动分析系统异常日志_Linux日志智能检测
Flexbox布局实践:实现底部页脚与顶部粘性导航条的完美结合
《edge浏览器》关闭翻译功能方法
yy漫画登录页面官方入口_yy漫画在线阅读网址入口
雨课堂官网在线登录 网页版雨课堂登录链接
sublime text 4如何安装_最新版sublime下载与汉化教程
《长生:天机降世》火塔小怪大全
Coolpad5890 ROM刷机包
《海贝音乐》均衡器设置方法
自定义你的VS Code状态栏,监控关键信息
深入理解随机递归函数的确定性:内部节点、叶节点与时间复杂度分析
秋风萧瑟洪波涌起中的萧瑟指的是什么
漫蛙漫画直连入口 _ manwa官方备用入口实时检测
百度小说看书时如何翻页_百度小说手动翻页与自动翻页设置
从HTML表单获取逗号分隔值并转换为NumPy数组进行预测
Lar*el怎么实现全文搜索_Lar*el Scout集成Algolia教程
PHP魔术方法__set与__isset:设计考量、性能权衡与静态分析的视角
哔哩哔哩黑名单怎么查看
C++ priority_queue怎么用_C++优先队列底层实现与自定义比较器
Win10关闭UAC用户账户控制的方法 Win10降低安全提示等级【技巧】
百度地图离线地图无法加载如何解决 百度地图离线地图加载优化方法
C++如何使用CMake构建项目_C++ CMakeLists.txt编写入门教程
如何修改Windows截图的默认保存位置_告别C盘让桌面更整洁【教程】
《撕歌》会员开通方法
《procreate》绘制渐变效果教程
我的世界官方网址入口 我的世界游戏主页直达入口
画质怪兽120帧安卓和平精英免费版
B站怎么开|直播| B站|直播|申请需要什么条件【新手必看】
J*a中的值传递到底指什么_值传递模型在参数传递中的真正含义说明
123网页端官方登录页 123邮箱网页版即时通讯服务
Python csv 模块处理非字符串数据:列表写入 CSV 文件的机制解析
163邮箱网页版入口 163邮箱在线使用
J*aScript事件处理:优化键盘输入与表单提交的实践指南
PHP实现等比数列:构建数组元素基于前一个值递增的方法
《虎扑》关闭社区内容推荐方法
解决Windows上Composer PATH变量冲突导致的命令无法识别问题
React应用中Commerce.js数据加载与状态管理最佳实践
《顺丰同城骑士》查看我的技能方法
mysql怎么导入sql文件_mysql导入sql文件的方法与技巧
邮政快递寄件查询入口 邮政快递收件查询入口
铁路12306官网入口 铁路12306中国铁路官网登录首页
2025-11-29
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。