package utils import ( "fmt" "io" "net/http" "os" "strings" "time" ) func DownloadFile(url string, path string) (bool, string) { // 创建HTTP客户端 client := &http.Client{ Timeout: 5 * time.Second, } return DownloadFileWithClient(client, url, path) } func DownloadFileWithClient(client *http.Client, url string, path string) (bool, string) { return downloadWithProgress(client, url, path) } // 带进度显示的下载函数 func downloadWithProgress(client *http.Client, url, dest string) (bool, string) { // 创建目标文件 file, err := os.Create(dest) if err != nil { return false, fmt.Sprintf("创建文件失败: %w", err) } defer file.Close() // 发起请求 resp, err := client.Get(url) if err != nil { return false, fmt.Sprintf("HTTP请求失败: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return false, fmt.Sprintf("服务器返回错误状态码: %s", resp.Status) } // 获取文件大小 size := resp.ContentLength var downloaded int64 // 打印下载信息 fmt.Printf("开始下载文件: %s 文件大小: %s\n", url, HumanSizeInt(size)) // 创建带进度跟踪的Reader progressReader := &progressReader{ Reader: resp.Body, Reporter: func(r int64) { downloaded += r printProgress(downloaded, size) }, } // 执行拷贝 if _, err := io.Copy(file, progressReader); err != nil { return false, fmt.Sprintf("文件拷贝失败: %w", err) } fmt.Print("\n") // 保持最后进度显示的完整性 return true, fmt.Sprintf("文件下载成功: %s", dest) } // 进度跟踪Reader type progressReader struct { io.Reader Reporter func(r int64) } func (pr *progressReader) Read(p []byte) (int, error) { n, err := pr.Reader.Read(p) if n > 0 { pr.Reporter(int64(n)) } return n, err } // 打印进度信息 func printProgress(downloaded, total int64) { const barLength = 40 percent := float64(downloaded) / float64(total) * 100 // 生成进度条 filled := int(barLength * downloaded / total) bar := fmt.Sprintf("[%s%s]", strings.Repeat("=", filled), strings.Repeat(" ", barLength-filled)) // 格式化为人类可读大小 humanSize := func(bytes int64) string { const unit = 1024 if bytes < unit { return fmt.Sprintf("%d B", bytes) } div, exp := int64(unit), 0 for n := bytes / unit; n >= unit; n /= unit { div *= unit exp++ } return fmt.Sprintf("%.1f %ciB", float64(bytes)/float64(div), "KMGTPE"[exp]) } fmt.Printf("\r%-45s %6.2f%% %s/%s", bar, percent, humanSize(downloaded), humanSize(total)) }