improve the built-in download function, enhance the logic robustness and reliability in adverse network environments

This commit is contained in:
josc146 2023-06-05 22:54:36 +08:00
parent f373f1caa8
commit 4fb35845b0

View File

@ -1,6 +1,7 @@
package backend_golang package backend_golang
import ( import (
"context"
"path/filepath" "path/filepath"
"time" "time"
@ -18,6 +19,7 @@ func (a *App) DownloadFile(path string, url string) error {
type DownloadStatus struct { type DownloadStatus struct {
resp *grab.Response resp *grab.Response
cancel context.CancelFunc
Name string `json:"name"` Name string `json:"name"`
Path string `json:"path"` Path string `json:"path"`
Url string `json:"url"` Url string `json:"url"`
@ -29,7 +31,7 @@ type DownloadStatus struct {
Done bool `json:"done"` Done bool `json:"done"`
} }
var downloadList []DownloadStatus var downloadList []*DownloadStatus
func existsInDownloadList(url string) bool { func existsInDownloadList(url string) bool {
for _, ds := range downloadList { for _, ds := range downloadList {
@ -41,49 +43,58 @@ func existsInDownloadList(url string) bool {
} }
func (a *App) PauseDownload(url string) { func (a *App) PauseDownload(url string) {
for i, ds := range downloadList { for _, ds := range downloadList {
if ds.Url == url { if ds.Url == url {
if ds.resp != nil { if ds.cancel != nil {
ds.resp.Cancel() ds.cancel()
}
downloadList[i] = DownloadStatus{
resp: ds.resp,
Name: ds.Name,
Path: ds.Path,
Url: ds.Url,
Downloading: false,
} }
ds.resp = nil
ds.Downloading = false
ds.Speed = 0
break
} }
} }
} }
func (a *App) ContinueDownload(url string) { func (a *App) ContinueDownload(url string) {
for i, ds := range downloadList { for _, ds := range downloadList {
if ds.Url == url { if ds.Url == url {
client := grab.NewClient() if !ds.Downloading && ds.resp == nil && !ds.Done {
req, _ := grab.NewRequest(ds.Path, ds.Url) ds.Downloading = true
resp := client.Do(req)
downloadList[i] = DownloadStatus{ req, err := grab.NewRequest(ds.Path, ds.Url)
resp: resp, if err != nil {
Name: ds.Name, ds.Downloading = false
Path: ds.Path, break
Url: ds.Url, }
Downloading: true, // if PauseDownload() is called before the request finished, ds.Downloading will be false
// if the user keeps clicking pause and resume, it may result in multiple requests being successfully downloaded at the same time
// so we have to create a context and cancel it when PauseDownload() is called
ctx, cancel := context.WithCancel(context.Background())
ds.cancel = cancel
req = req.WithContext(ctx)
resp := grab.DefaultClient.Do(req)
if resp != nil && resp.HTTPResponse != nil &&
resp.HTTPResponse.StatusCode >= 200 && resp.HTTPResponse.StatusCode < 300 {
ds.resp = resp
} else {
ds.Downloading = false
}
} }
break
} }
} }
} }
func (a *App) AddToDownloadList(path string, url string) { func (a *App) AddToDownloadList(path string, url string) {
if !existsInDownloadList(url) { if !existsInDownloadList(url) {
downloadList = append(downloadList, DownloadStatus{ downloadList = append(downloadList, &DownloadStatus{
resp: nil, resp: nil,
Name: filepath.Base(path), Name: filepath.Base(path),
Path: a.exDir + path, Path: a.exDir + path,
Url: url, Url: url,
Downloading: true, Downloading: false,
}) })
a.ContinueDownload(url) a.ContinueDownload(url)
} else { } else {
@ -96,32 +107,17 @@ func (a *App) downloadLoop() {
go func() { go func() {
for { for {
<-ticker.C <-ticker.C
for i, ds := range downloadList { for _, ds := range downloadList {
transferred := int64(0)
size := int64(0)
speed := float64(0)
progress := float64(0)
downloading := ds.Downloading
done := false
if ds.resp != nil { if ds.resp != nil {
transferred = ds.resp.BytesComplete() ds.Transferred = ds.resp.BytesComplete()
size = ds.resp.Size() ds.Size = ds.resp.Size()
speed = ds.resp.BytesPerSecond() ds.Speed = ds.resp.BytesPerSecond()
progress = 100 * ds.resp.Progress() ds.Progress = 100 * ds.resp.Progress()
downloading = !ds.resp.IsComplete() ds.Downloading = !ds.resp.IsComplete()
done = ds.resp.Progress() == 1 ds.Done = ds.resp.Progress() == 1
} if !ds.Downloading {
downloadList[i] = DownloadStatus{ ds.resp = nil
resp: ds.resp, }
Name: ds.Name,
Path: ds.Path,
Url: ds.Url,
Transferred: transferred,
Size: size,
Speed: speed,
Progress: progress,
Downloading: downloading,
Done: done,
} }
} }
runtime.EventsEmit(a.ctx, "downloadList", downloadList) runtime.EventsEmit(a.ctx, "downloadList", downloadList)