153 lines
3.8 KiB
Go
153 lines
3.8 KiB
Go
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))
|
||
}
|