diff --git a/app.go b/app.go index 140e539..2d9c7e0 100644 --- a/app.go +++ b/app.go @@ -3,6 +3,7 @@ package main import ( "MacFastLookup/service" "context" + "fmt" "os" "path/filepath" @@ -37,7 +38,8 @@ func (a *App) StartDownload() { os.Mkdir(path, 0755) } - service.DownloadFile("https://tools.taurusxin.com/macfastlookup/mac_vendors.sqlite3", dbFilePath, true, func(progress, total int64) { - runtime.EventsEmit(a.ctx, "download-progress", float32(progress / total)) + service.EnhancedDownload("https://tools.taurusxin.com/macfastlookup/mac_vendors.sqlite3", dbFilePath, true, func(downloaded, total, rate int64, packageName string) { + fmt.Printf("\rDownloading[%s][ %d%% ][%d/%d]", packageName, rate, downloaded, total) + runtime.EventsEmit(a.ctx, "download-progress", rate) }) } \ No newline at end of file diff --git a/frontend/src/pages/Download.tsx b/frontend/src/pages/Download.tsx index 68ce902..262ed54 100644 --- a/frontend/src/pages/Download.tsx +++ b/frontend/src/pages/Download.tsx @@ -27,8 +27,8 @@ const Download = (props: { onFinish: () => void }) => { console.log(res) }) EventsOn('download-progress', (progress: number) => { - setDownloadProgress(progress * 100) - if (progress === 1) { + setDownloadProgress(progress) + if (progress === 100) { props.onFinish() setTitle('Completed') setIsDisabled(false) diff --git a/service/download.go b/service/download.go index 2c9acf0..ae8fbd0 100644 --- a/service/download.go +++ b/service/download.go @@ -1,83 +1,84 @@ package service import ( - "fmt" - "io" - "net/http" - "os" - "strconv" + "fmt" + "io" + "net/http" + "os" + "path/filepath" ) -// ProgressWriter is a custom writer to track progress and execute a callback -type ProgressWriter struct { - io.Writer - Total int64 - Progress int64 - Callback func(progress int64, total int64) +// Counter 接口定义了进度追踪器应实现的方法 +type Counter interface { + Write(p []byte) (int, error) + SetTotal(total int64) } -func (pw *ProgressWriter) Write(p []byte) (int, error) { - n, err := pw.Writer.Write(p) - if err != nil { - return n, err - } - pw.Progress += int64(n) - pw.Callback(pw.Progress, pw.Total) - return n, nil +// WriteCounter 实现了 Counter 接口 +type WriteCounter struct { + Total int64 + Current int64 + Rate int64 + Package string + ProgressCb ProgressCallback } -func DownloadFile(url string, filePath string, overwrite bool, callback func(progress int64, total int64)) error { - // Check if the file exists - if _, err := os.Stat(filePath); err == nil { - if overwrite { - // Overwrite the file if it exists - if err := os.Remove(filePath); err != nil { - return err - } - } else { - return fmt.Errorf("file %s already exists", filePath) - } - } +// ProgressCallback 是用于报告下载进度的函数类型 +type ProgressCallback func(bytesDownloaded, totalBytes int64, rate int64, packageName string) - // Create the file - out, err := os.Create(filePath) - if err != nil { - return err - } - defer out.Close() - - // Get the data - resp, err := http.Get(url) - if err != nil { - return err - } - defer resp.Body.Close() - - - // Check server response - if resp.StatusCode != http.StatusOK { - return fmt.Errorf("bad status: %s", resp.Status) - } - - // Get the size - size, err := strconv.Atoi(resp.Header.Get("Content-Length")) - if err != nil { - return err - } - - // Create progress writer - pw := &ProgressWriter{ - Writer: out, - Total: int64(size), - Callback: callback, - } - - // Write the body to file with progress - _, err = io.Copy(pw, resp.Body) - if err != nil { - return err - } - - fmt.Println("\nDownload complete") - return nil +func (w *WriteCounter) Write(p []byte) (int, error) { + n := len(p) + w.Current += int64(n) + w.Rate = w.Current * 100 / w.Total + if w.ProgressCb != nil { + w.ProgressCb(w.Current, w.Total, w.Rate, w.Package) + } + return n, nil } + +func (w *WriteCounter) SetTotal(total int64) { + w.Total = total +} + +// EnhancedDownload 下载文件并报告进度 +func EnhancedDownload(url string, path string, overwrite bool, progressCb ProgressCallback) error { + // 如果路径为空,使用URL中的文件名 + if path == "" { + path = filepath.Base(url) + } + + // 检查文件是否已存在 + if _, err := os.Stat(path); err == nil && !overwrite { + return fmt.Errorf("文件 %s 已存在且未设置覆盖", path) + } + + // 创建HTTP请求 + resp, err := http.Get(url) + if err != nil { + return fmt.Errorf("request %s error: %v", url, err) + } + defer resp.Body.Close() + + // 创建输出文件 + out, err := os.Create(path) + if err != nil { + return err + } + defer out.Close() + + // 创建一个WriteCounter来跟踪进度 + counter := &WriteCounter{ + Total: resp.ContentLength, + Package: filepath.Base(path), + ProgressCb: progressCb, + } + + // 使用TeeReader来同时写入文件和更新进度 + _, err = io.Copy(out, io.TeeReader(resp.Body, counter)) + if err != nil { + return fmt.Errorf("write error: %v", err) + } + + fmt.Printf("\ndownload [ %v ] -> [ %s ] success\n", url, path) + return nil +} \ No newline at end of file