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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
| package main
import ( "fmt" "github.com/vbauerster/mpb/v5" "github.com/vbauerster/mpb/v5/decor" "io" "net/http" "os" "runtime" "strconv" "sync" )
type Resource struct { Filename string Url string }
type Downloader struct { wg *sync.WaitGroup pool chan *Resource Concurrent int HttpClient http.Client TargetDir string Resources []Resource }
func NewDownloader(targetDir string) *Downloader { concurrent := runtime.NumCPU() return &Downloader{ wg: &sync.WaitGroup{}, TargetDir: targetDir, Concurrent: concurrent, } }
func (d *Downloader) AppendResource(filename, url string) { d.Resources = append(d.Resources, Resource{ Filename: filename, Url: url, }) }
func (d *Downloader) Download(resource Resource, progress *mpb.Progress) error { defer d.wg.Done() d.pool <- &resource finalPath := d.TargetDir + "/" + resource.Filename target, err := os.Create(finalPath + ".tmp") if err != nil { return err }
req, err := http.NewRequest(http.MethodGet, resource.Url, nil) if err != nil { return err }
resp, err := http.DefaultClient.Do(req) if err != nil { target.Close() return err } defer resp.Body.Close() fileSize, _ := strconv.Atoi(resp.Header.Get("Content-Length")) bar := progress.AddBar( int64(fileSize), mpb.PrependDecorators( decor.CountersKibiByte("% .2f / % .2f"), decor.Percentage(decor.WCSyncSpace), ), mpb.AppendDecorators( decor.EwmaETA(decor.ET_STYLE_GO, 90), decor.Name(" ] "), decor.EwmaSpeed(decor.UnitKiB, "% .2f", 60), ), ) reader := bar.ProxyReader(resp.Body) defer reader.Close() if _, err := io.Copy(target, reader); err != nil { target.Close() return err }
target.Close() if err := os.Rename(finalPath+".tmp", finalPath); err != nil { return err } <-d.pool return nil }
func (d *Downloader) Start() error { d.pool = make(chan *Resource, d.Concurrent) fmt.Println("开始下载,当前并发:", d.Concurrent) p := mpb.New(mpb.WithWaitGroup(d.wg)) for _, resource := range d.Resources { d.wg.Add(1) go d.Download(resource, p) } p.Wait() d.wg.Wait() return nil }
|