细节调整及优化(26.8.3)
This commit is contained in:
@@ -21,15 +21,35 @@ const READ_STALL_TIMEOUT: std::time::Duration = std::time::Duration::from_secs(3
|
||||
/// HTTP/HTTPS 下载器
|
||||
#[derive(Clone)]
|
||||
pub struct HttpDownloader {
|
||||
client: Client,
|
||||
/// 默认客户端:尊重系统代理(reqwest 默认行为,mihomo 开启系统代理时经其转发)
|
||||
system_client: Client,
|
||||
/// 直连客户端:强制禁用系统代理(no_proxy)
|
||||
direct_client: Client,
|
||||
}
|
||||
|
||||
impl HttpDownloader {
|
||||
pub fn new() -> Self {
|
||||
let client = Client::builder()
|
||||
let system_client = Client::builder()
|
||||
.build()
|
||||
.unwrap_or_else(|_| Client::new());
|
||||
Self { client }
|
||||
let direct_client = Client::builder()
|
||||
// 强制直连:即使系统代理已开启,下载也不经过代理
|
||||
.no_proxy()
|
||||
.build()
|
||||
.unwrap_or_else(|_| Client::new());
|
||||
Self {
|
||||
system_client,
|
||||
direct_client,
|
||||
}
|
||||
}
|
||||
|
||||
/// 根据 use_proxy 选择客户端
|
||||
fn client(&self, use_proxy: bool) -> &Client {
|
||||
if use_proxy {
|
||||
&self.system_client
|
||||
} else {
|
||||
&self.direct_client
|
||||
}
|
||||
}
|
||||
|
||||
/// 探测下载资源信息(大小、是否支持 Range、文件名)
|
||||
@@ -38,10 +58,11 @@ impl HttpDownloader {
|
||||
&self,
|
||||
url: &str,
|
||||
headers: &HashMap<String, String>,
|
||||
use_proxy: bool,
|
||||
) -> Result<ProbeResult, String> {
|
||||
let client = self.client(use_proxy);
|
||||
// 先尝试 Range 请求(能同时判断 Accept-Ranges 和获取大小)
|
||||
let mut req = self
|
||||
.client
|
||||
let mut req = client
|
||||
.get(url)
|
||||
.header("Range", "bytes=0-0")
|
||||
.header("User-Agent", "Thing-Download-Engine/1.0");
|
||||
@@ -97,7 +118,7 @@ impl HttpDownloader {
|
||||
}
|
||||
Err(_) => {
|
||||
// GET 失败,尝试 HEAD 作为回退
|
||||
let mut head_req = self.client.head(url);
|
||||
let mut head_req = client.head(url);
|
||||
for (k, v) in headers {
|
||||
head_req = head_req.header(k, v);
|
||||
}
|
||||
@@ -132,6 +153,7 @@ impl HttpDownloader {
|
||||
/// - `cancel`: 取消标志
|
||||
/// - `progress`: 每个分段的已下载字节(AtomicU64,与 segments 一一对应)
|
||||
/// - `limiter`: 全局限速器
|
||||
/// - `use_proxy`: 是否使用系统代理(false=强制直连)
|
||||
pub async fn download(
|
||||
&self,
|
||||
url: &str,
|
||||
@@ -141,7 +163,9 @@ impl HttpDownloader {
|
||||
cancel: Arc<AtomicBool>,
|
||||
progress: &[Arc<AtomicU64>],
|
||||
limiter: Arc<RateLimiter>,
|
||||
use_proxy: bool,
|
||||
) -> Result<(), String> {
|
||||
let client = self.client(use_proxy);
|
||||
let total_size = segments.iter().map(|s| s.len()).sum();
|
||||
|
||||
// 预分配文件(若已知大小)
|
||||
@@ -167,7 +191,7 @@ impl HttpDownloader {
|
||||
// 单线程下载(不支持 Range 或文件太小)
|
||||
let seg = &segments[0];
|
||||
let prog = &progress[0];
|
||||
self.download_segment(url, headers, seg, file_path, cancel.clone(), prog.clone(), limiter)
|
||||
self.download_segment(url, headers, seg, file_path, cancel.clone(), prog.clone(), limiter, client)
|
||||
.await?;
|
||||
return Ok(());
|
||||
}
|
||||
@@ -187,7 +211,7 @@ impl HttpDownloader {
|
||||
.unwrap_or_default();
|
||||
let limiter = limiter.clone();
|
||||
let file_path = file_path.to_path_buf();
|
||||
let client = self.client.clone();
|
||||
let client = client.clone();
|
||||
|
||||
join_set.spawn(async move {
|
||||
download_segment_with_client(
|
||||
@@ -241,9 +265,10 @@ impl HttpDownloader {
|
||||
cancel: Arc<AtomicBool>,
|
||||
progress: Arc<AtomicU64>,
|
||||
limiter: Arc<RateLimiter>,
|
||||
client: &Client,
|
||||
) -> Result<(), String> {
|
||||
download_segment_with_client(
|
||||
&self.client,
|
||||
client,
|
||||
url,
|
||||
headers,
|
||||
seg,
|
||||
@@ -366,6 +391,15 @@ async fn download_segment_with_client(
|
||||
limiter.consume(buf.len() as u64).await;
|
||||
buf.clear();
|
||||
}
|
||||
// 校验:已知大小的分段若流提前结束(收到的字节数不足分段长度),
|
||||
// 说明服务器提前断开或返回不完整内容,不能标记为完成,否则文件会被截断
|
||||
if !unknown_size && local_completed < seg.len() {
|
||||
return Err(format!(
|
||||
"文件不完整:已接收 {} / {} 字节,服务器提前结束连接",
|
||||
local_completed,
|
||||
seg.len()
|
||||
));
|
||||
}
|
||||
break;
|
||||
}
|
||||
Err(_) => {
|
||||
|
||||
Reference in New Issue
Block a user