目标

使用go原生http库下载时,显示实时下载进度。

How

Go的Http是一个流,通过截取流的信息可以完成显示下载进度的目的。

  1. 首先定义一个接口
1
2
3
4
type Counter interface {
	Write(p []byte) (int, error)
	SetTotal(total int64)
}
  1. 定义接口实现
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
type WriteCounter struct {
	Total   int64  `json:"total"`
	Current int64  `json:"current"`
	Rate    int64  `json:"rate"`
	Package string `json:"package"`
}

func (w *WriteCounter) Write(p []byte) (int, error) {
	n := len(p)
	w.Current += int64(n)
	w.Rate = w.Current * 100 / w.Total
  fmt.Printf("\rDownloading[%s][ %s%% ][%d/%d] B complete",w.Package,w.Rate, w.Current,w.Total)
	return n, nil
}

func (w *WriteCounter) SetTotal(total int64) {
	d.Total = total
}
  1. 定义下载逻辑
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
func Download(counter IOCounter, url, path string) error {
	resp, err := http.Get(url)
	if err != nil {
		logger.Errorf("request %s error: %v", url, err)
		return err
	}
	defer func() {
		closeError := resp.Body.Close()
		if closeError != nil {
			logger.Errorf("close error: %v", closeError)
			return
		}
	}()

	counter.SetTotal(resp.ContentLength)
	out, err := os.Create(path)
	if err != nil {
		return err
	}
	defer out.Close()
	if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil {
		logger.Errorf("write error: %v", err)
	}
	logger.Infof("download [ %v ]->[ %s ] success", url, path)
	return nil
}