fix: 更新 DownloadFile 函数以使用 fasthttp 客户端,优化下载性能并简化错误处理逻辑

This commit is contained in:
lixiangwuxian
2025-04-10 02:15:34 +08:00
parent b42b3ca4d3
commit cc40ea8b3d
3 changed files with 52 additions and 45 deletions

View File

@@ -2,16 +2,16 @@ package util
import (
"bytes"
"crypto/tls"
"fmt"
"io"
"net/http"
"net/url"
"os"
"path"
"strings"
"time"
"github.com/google/uuid"
"github.com/valyala/fasthttp"
)
// GetImageExtension 根据文件头检测图片格式并返回对应的扩展名
@@ -66,60 +66,44 @@ func normalizeURL(rawURL string) string {
// DownloadFile 下载文件到指定目录,返回带有正确扩展名的完整文件路径
func DownloadFile(urlStr string, dirPath string) (filepath string, err error) {
// 解析URL获取域名
parsedURL, err := url.Parse(urlStr)
if err != nil {
return "", fmt.Errorf("URL解析失败: %v", err)
// 创建fasthttp客户端
client := &fasthttp.Client{
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
MaxConnWaitTimeout: 30 * time.Second,
TLSConfig: nil, // 使用默认配置
}
// 创建基础传输配置
transport := &http.Transport{
ForceAttemptHTTP2: false, // 服务器不支持HTTP/2
TLSClientConfig: &tls.Config{
InsecureSkipVerify: false, // 启用证书验证
MinVersion: tls.VersionTLS12,
MaxVersion: tls.VersionTLS12,
CipherSuites: []uint16{
tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
},
NextProtos: []string{"http/1.1"}, // 禁用ALPN协商
},
}
// 准备请求
req := fasthttp.AcquireRequest()
defer fasthttp.ReleaseRequest(req)
// 如果是腾讯的特定域名使用特殊的TLS配置
if strings.HasSuffix(parsedURL.Host, ".qq.com.cn") {
transport.DisableCompression = true // 服务器不支持压缩
transport.TLSClientConfig.ServerName = parsedURL.Host // 确保SNI正确
}
req.SetRequestURI(urlStr)
req.Header.SetMethod("GET")
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")
// 创建HTTP客户端
client := &http.Client{
Transport: transport,
}
// 准备响应
resp := fasthttp.AcquireResponse()
defer fasthttp.ReleaseResponse(resp)
// 发送 HTTP GET 请求
var resp *http.Response
// 发送请求
var maxRetry = 100
var retry = 0
for resp, err = client.Get(urlStr); err != nil && retry < maxRetry; resp, err = client.Get(urlStr) {
for err = client.Do(req, resp); err != nil && retry < maxRetry; err = client.Do(req, resp) {
fmt.Printf("下载失败,正在重试... 错误: %v\n", err)
retry++
}
if err != nil {
return "", fmt.Errorf("下载失败: %v", err)
}
defer resp.Body.Close()
// 检查 HTTP 响应状态码
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("请求失败,状态码: %d", resp.StatusCode)
// 检查响应状态码
if resp.StatusCode() != fasthttp.StatusOK {
return "", fmt.Errorf("请求失败,状态码: %d", resp.StatusCode())
}
// 取响应内容到内存中以检测文件类型
bodyData, err := io.ReadAll(resp.Body)
if err != nil {
return "", fmt.Errorf("读取响应内容失败: %v", err)
}
// 取响应
bodyData := resp.Body()
// 获取正确的文件扩展名
ext := GetImageExtension(bodyData)