Скачивание файла с указанием оставшегося времени

пишу небольшой скрипт, который будет скачивать большой файл, пока что получилось сделать по примеру:

package main

import (
    "fyne.io/fyne/v2/app"
    "fyne.io/fyne/v2/widget"
    "io"
    "net/http"
    "os"
)

func main() {
    fileUrl := "https://golangcode.com/logo.svg"
    err := DownloadFile("logo.svg", fileUrl)
    if err != nil {
        panic(err)
    }
    a := app.New()
    w := a.NewWindow("Test")
    w.SetContent(widget.NewLabel("Downloaded: " + fileUrl))
    w.ShowAndRun()
}

func DownloadFile(filepath string, url string) error {

    // Get the data
    resp, err := http.Get(url)

    if err != nil {
        return err
    }
    defer resp.Body.Close()

    // Create the file
    out, err := os.Create(filepath)
    if err != nil {
        return err
    }
    defer out.Close()

    // Write the body to file
    _, err = io.Copy(out, resp.Body)
    return err
}

По коду понятно, что скачивается файл https://golangcode.com/logo.svg и сохраняется там, где запускается скрипт.

Так же есть a := app.New(), который, в будущем, будет придавать более красивый дизайн, чем обычная консоль.

Вот тут и возникает вопрос о времени скачивания, хотел бы сделать прогресс бар, в котором бы показывалось оставшееся время скачивания, вроде нашёл что-то вот такое: тык, где как раз и показываются проценты, но пока так и не разобрался, как это работает.


Ответы (1 шт):

Автор решения: Senior Pomidor

package main

import (
    "fmt"
    "io"
    "net/http"
    "os"
    "strconv"
    "time"

    "fyne.io/fyne/v2/app"
    "fyne.io/fyne/v2/container"
    "fyne.io/fyne/v2/widget"
)

var pb *widget.ProgressBar

func main() {
    fmt.Println("Download Started")

    a := app.New()
    w := a.NewWindow("Test")

    pb = widget.NewProgressBar()
    w.SetContent(container.NewVBox(pb))

    // запускаю в отдельной горутине, так как w.ShowAndRun() зависает до завершения процесса
    go func() {
        // картинка чуть тяжелее 
        fileUrl := "https://upload.wikimedia.org/wikipedia/commons/d/d6/Wp-w4-big.jpg"
        err := DownloadFile("avatar.jpg", fileUrl)
        if err != nil {
            panic(err)
        }
    }()

    w.ShowAndRun()
    fmt.Println("Download Finished")
}

type WriteCounter struct {
    Total         uint64
    ContentLength float64
}

func (wc *WriteCounter) Write(p []byte) (int, error) {
    n := len(p)
    wc.Total += uint64(n)
    wc.PrintProgress()
    return n, nil
}

func (wc WriteCounter) PrintProgress() {
    // добавил слип чтобы увидеть прогресс бар. он тут нужен только для анимации. можете удалить. 
    time.Sleep(time.Millisecond * 100)
    fmt.Printf("\rDownloading... %d complete", wc.Total)
    // SetValue отображает проценты. поэтому мы считаем процент от всего объема данных
    pb.SetValue(float64(wc.Total) / wc.ContentLength)
    // перерисовываем
    pb.Refresh()
}

func DownloadFile(filepath string, url string) error {
    out, err := os.Create(filepath + ".tmp")
    if err != nil {
        return err
    }

    // Get the data
    resp, err := http.Get(url)
    if err != nil {
        out.Close()
        return err
    }
    defer resp.Body.Close()

    contentLength, err := strconv.ParseFloat(resp.Header.Get("content-length"), 64)
    if err != nil {
        return err
    }

    counter := &WriteCounter{ContentLength: contentLength}
    if _, err = io.Copy(out, io.TeeReader(resp.Body, counter)); err != nil {
        out.Close()
        return err
    }

    fmt.Print("\n")

    out.Close()

    if err = os.Rename(filepath+".tmp", filepath); err != nil {
        return err
    }
    return nil
}

→ Ссылка