? ? ? 在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的時候同步將數據打印到標準輸出中。
package mainimport ("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測試用例
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對象
// 自定義的用來統計和顯示下載進度的對象
type DownProgress struct {CLen uint64 //數據的總長度Total uint64 // 用來記錄數據的總寫入長度
}// 注意,這個方法只有在treeReader中的數據被讀取之后才會被調用, 每讀一次這里調用一次
func (w *DownProgress) Write(b []byte) (int, error) {n := len(b) // number of bytes writtenw.Total += uint64(n)// 打印下載進度// 獲取當前下載進度百分比,需要將除數和被除數數據類型都轉為float64percent := float64(w.Total) / float64(w.CLen) * 100// 打印當前下載進度 注意這里的 %.2f 表示格式化浮點數 2位小數, 后面的 %% 表示在顯示一個百分號 %fmt.Printf("\r下載中... 已完成百分比 %.2f%% ", percent)// 打印下載進度 endreturn n, nil
}
自定義Writer的使用
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()// 自定義一個用來統計下載進度的WriterdownProgress := &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%?
?
還有其他的用法? 等待你的發現和探索。。。。。。