package utils import ( "agent-wdd/log" "fmt" "io" "net/http" "os" "os/exec" "path/filepath" "strings" "time" ) func DownloadFile(url string, path string) (bool, string) { // 创建HTTP客户端 client := &http.Client{ Timeout: 5 * time.Second, } return DownloadFileWithClient(client, url, path) } // DownloadFileWithClient 使用http客户端下载文件 func DownloadFileWithClient(client *http.Client, url string, path string) (bool, string) { // path如果是一个目录,则需要获取文件名 // 获取url使用 / 分割最后的一部分 // 如果path是目录,则需要获取文件名 if IsDirOrFile(path) { fileName := strings.Split(url, "/")[len(strings.Split(url, "/"))-1] path = filepath.Join(path, fileName) log.Info("path是目录,自动获取文件名为 => : %s", path) } 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("创建文件失败: %s", err.Error()) } defer file.Close() // 发起请求 resp, err := client.Get(url) if err != nil { return false, fmt.Sprintf("HTTP请求失败: %s", err.Error()) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return false, fmt.Sprintf("服务器返回错误状态码: %s", resp.Status) } // 获取文件大小 size := resp.ContentLength var downloaded int64 // 不支持下载超过10GB的文件 if size > 10*1024*1024*1024 || size < 0 { log.Error("文件大小超过10GB,或者文件大小未知,不支持高级下载方式! 尝试使用wget下载") return downloadFileByWget(url, dest) } // 打印下载信息 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("文件拷贝失败: %s", err.Error()) } fmt.Print("\n") // 保持最后进度显示的完整性 return true, fmt.Sprintf("文件下载成功: %s", dest) } // 使用wget下载文件 func downloadFileByWget(url, dest string) (bool, string) { log.Info("使用wget下载文件: %s", fmt.Sprintf("wget %s -qO %s", url, dest)) cmd := exec.Command("wget", url, "-qO", dest) _, err := cmd.CombinedOutput() if err != nil { return false, fmt.Sprintf("wget下载失败: %s", err.Error()) } // 检查文件是否存在且不为空 fileInfo, err := os.Stat(dest) if err != nil || fileInfo.Size() == 0 { return false, fmt.Sprintf("wget下载失败 文件不存在或为空: %s", dest) } return true, fmt.Sprintf("wget下载成功: %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)) }