go语言内置io包中TreeReader函数的理解和使用示例

在go语言的内置io包中的这个 TreeReader函数,函数原型 func TeeReader(r Reader, w Writer) Reader 从函数原型中看是给他一个Reader, 和一个Writer 然后他给你返回一个Reader, 本文中我们把这个返回的Reader叫做 treeReader, 他是一个很特别的reader,。

他的作用就是将你输入的 r 和w建立一个对应关系, 当你在读取这个treeReader的时候,他会将读取到的数据同时写入到w中。同时在他的内部没有缓存,写入必须在读取完成之前完成。写入时遇到的任何错误都会报告为读取错误。

这个是官方的解释: " TeeReader returns a Reader that writes to w what it reads from r. All reads from r performed through it are matched with corresponding writes to w. There is no internal buffering - the write must complete before the read completes. Any error encountered while writing is reported as a read error. " 对于非英语母语的人来说看起来是不是有点绕啊? 😆

知道了他的作用, 那我们就可以根据他的作用来给他找合适的用处了, 我们一般用它来统计文件下载进度, 还有计算文件的HASH值等。

先看看官方给的小示例

这里的用途是在读取treeReader的时候同步将数据打印到标准输出中。

Go 复制代码
package main

import (
	"io"
	"log"
	"os"
	"strings"
)

func main() {
	var r io.Reader = strings.NewReader("some io.Reader stream to be read\n")

	r = io.TeeReader(r, os.Stdout)

	// Everything read from r will be copied to stdout.
	if _, err := io.ReadAll(r); err != nil {
		log.Fatal(err)
	}

}

使用io.ReeeReader来计算文件的hash值 sha256 Example测试用例

Go 复制代码
func ExampleCalSha() {
	file, err := os.Open("testdata/test.zip")
	if err != nil {
		fmt.Println("error opening file: ", err)
		return
	}
	hash256 := sha256.New()
	treeReader := io.TeeReader(file, hash256)
	// 又拷贝回去
	io.Copy(file, treeReader) // 这个的意义就在于用它来从treeReader中读取数据的同时写入到hash256中

	b256 := hash256.Sum(nil)
	fmt.Printf("文件 %v SHA256: %X", file.Name(), b256) // output: a
}

使用io.TreeReader来实时显示文件的下载进度

这个很简单,我们只需要自定义一个Writer, 然后 利用TreeReader将下载文件的返回流 resp.Body和我们自定义的这个Writer建议对应关系, 然后我们再来读取这treeReader的数据, 在我们呢读取数据的时候也会同步的将数据发送到我们自定义的Writer中, 我们的下载进度显示就在我们自定义的Writer中完成。 我们可以使用 io.Copy(outfile, treeReader) 来从treeReader中读取数据并拷贝到outfile输出文件中。

自定义的用来显示下载进度的Writer对象

Go 复制代码
// 自定义的用来统计和显示下载进度的对象
type DownProgress struct {
	CLen  uint64 //数据的总长度
	Total uint64 // 用来记录数据的总写入长度
}

// 注意,这个方法只有在treeReader中的数据被读取之后才会被调用, 每读一次这里调用一次
func (w *DownProgress) Write(b []byte) (int, error) {
	n := len(b) // number of bytes written
	w.Total += uint64(n)

	// 打印下载进度
	// 获取当前下载进度百分比,需要将除数和被除数数据类型都转为float64
	percent := float64(w.Total) / float64(w.CLen) * 100
	// 打印当前下载进度 注意这里的 %.2f 表示格式化浮点数 2位小数, 后面的 %% 表示在显示一个百分号 %
	fmt.Printf("\r下载中...  已完成百分比 %.2f%% ", percent)
	// 打印下载进度 end

	return n, nil
}

自定义Writer的使用

Go 复制代码
    resp,err:=http.DefaultClient.Get("https://cn.bing.com/th?id=OHR.MPPUnesco_ZH-CN8076198158_UHD.jpg")
	if err != nil {
		fmt.Println("request error: ", err)
		return
	}
	defer resp.Body.Close()

// 创建保存文件
	outfile, err := os.Create("abc.jpg")
	if err != nil {
		return err
	}
	defer outfile.Close()

   // 自定义一个用来统计下载进度的Writer
    downProgress := &DownProgress{CLen: uint64(resp.ContentLength)} // 下载进度显示对象, 这里利用io.TeeReader 将下载对象的数据和这个自定义的Writerd读取进行绑定, 在treeReader读取数据后会同步写入到这个自定义的write中,用来统计下载进度.

    treeReader := io.TeeReader(resp.Body, downProgress) //将自定义的writer和resp.Body建立联系

    io.Copy(outfile, treeReader) // 读取数据

执行后即可见到你的下载进度从0%到 100%显示

下载中... 已完成百分比 100.00%

还有其他的用法? 等待你的发现和探索。。。。。。

相关推荐
satan–0几秒前
R语言的下载、安装及环境配置(Rstudio&VSCode)
开发语言·windows·vscode·r语言
电饭叔33 分钟前
《python语言程序设计》2018版第8章19题几何Rectangle2D类(下)-头疼的几何和数学
开发语言·python
Eternal-Student34 分钟前
everyday_question dq20240731
开发语言·arm开发·php
卑微求AC1 小时前
(C语言贪吃蛇)11.贪吃蛇方向移动和刷新界面一起实现面临的问题
c语言·开发语言
程序猿小D1 小时前
第二百六十七节 JPA教程 - JPA查询AND条件示例
java·开发语言·前端·数据库·windows·python·jpa
Yvemil71 小时前
RabbitMQ 入门到精通指南
开发语言·后端·ruby
潘多编程1 小时前
Java中的状态机实现:使用Spring State Machine管理复杂状态流转
java·开发语言·spring
冷静 包容2 小时前
C语言学习之 没有重复项数字的全排列
c语言·开发语言·学习
碳苯2 小时前
【rCore OS 开源操作系统】Rust 枚举与模式匹配
开发语言·人工智能·后端·rust·操作系统·os
结衣结衣.2 小时前
C++ 类和对象的初步介绍
java·开发语言·数据结构·c++·笔记·学习·算法