改为请求-响应模式,每个 DownloadFile 只返回一个 FileChunk

This commit is contained in:
zqm
2026-04-10 13:28:41 +08:00
parent acb5760f38
commit 15c0a0419e

View File

@@ -824,7 +824,7 @@ fn handle_app_file_chunk(
data: &serde_json::Map<std::string::String, serde_json::Value>,
debug: bool,
) -> Option<(String, String)> {
// 注意其他应用的文件块filename 字段就是 relative_path
// 注意其他应用的文件块filename 字段就是 relative_path(服务端可能含 app/ 前缀)
let filename = data.get("filename").and_then(|v| v.as_str()).unwrap_or("");
if filename.is_empty() {
return None;
@@ -834,7 +834,17 @@ fn handle_app_file_chunk(
let chunk_data = data.get("data").and_then(|v| v.as_str()).unwrap_or("");
let is_last = data.get("is_last").and_then(|v| v.as_bool()).unwrap_or(false);
let key = format!("{}/{}", app_name, filename);
// 服务端 filename 可能含 app/ 前缀(如 "EasyTest/xxx.dll"),统一去掉前缀
// app_map 中的 key 用 app_name/relative_path 格式存储
let expected_prefix = format!("{}/", app_name);
let relative_path = if filename.starts_with(&expected_prefix) {
&filename[expected_prefix.len()..]
} else {
filename
};
let key = format!("{}/{}", app_name, relative_path);
// 记录原始 relative_path供 is_last=false 时请求下一块使用
let relative_path = relative_path.to_string();
let mut app_map = ctx.app_download_map.lock().unwrap();
// 获取当前正在下载的文件名(用于判断是否是新文件)
@@ -859,21 +869,20 @@ fn handle_app_file_chunk(
.and_then(|n| n.to_str())
.unwrap_or(filename);
let app_data_dir = get_updater_data_dir().join(app_name);
let _ = fs::create_dir_all(&app_data_dir);
let final_path = app_data_dir.join(tmp_filename);
let _ = fs::create_dir_all(final_path.parent().unwrap());
let temp_path = get_updater_data_dir()
.join("Updater")
.join("UpGrade")
.join(app_name)
.join(format!("{}.tmp", tmp_filename));
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [应用] 新文件: filename={}, tmp_path={:?}, parent_exists={}",
ts, filename, temp_path, temp_path.parent().map(|p| p.exists()).unwrap_or(false));
match File::create(&temp_path) {
Ok(file) => {
app_map.insert(key.clone(), DownloadState {
filename: filename.to_string(),
filename: relative_path.clone(), // 不带 app_name/ 前缀,与 current_filename 检查一致
offset: 0,
temp_path: Some(temp_path),
file: Some(file),
@@ -937,26 +946,27 @@ fn handle_app_file_chunk(
let final_path = upgrade_dir.join(final_filename);
let _ = fs::create_dir_all(final_path.parent().unwrap());
if let Err(e) = fs::rename(temp, &final_path) {
if debug {
eprintln!("[应用] 重命名失败 {}: {}", filename, e);
}
// 重命名同步操作Windows 上不会异步)
let rename_ok = fs::rename(temp, &final_path).is_ok();
if !rename_ok {
let _ = fs::copy(temp, &final_path);
let _ = fs::remove_file(temp);
}
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [应用] is_last=true: filename={}, temp={:?}, final={:?}, rename_ok={}",
ts, filename, temp, final_path, rename_ok);
if debug {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [应用] {} 下载完成,共 {} 字节", ts, filename, final_offset);
}
}
app_map.remove(&key);
drop(app_map); // 释放锁
Some((app_name.to_string(), filename.to_string()))
Some((app_name.to_string(), relative_path)) // 返回 relative_path
} else {
drop(app_map); // 释放锁
None
Some((app_name.to_string(), relative_path)) // 非最后一块也返回 relative_path供请求下一块使用
}
}
@@ -968,19 +978,27 @@ fn handle_app_download_complete(
size: u64,
debug: bool,
) -> Option<(String, String)> {
let key = format!("{}/{}", app_name, filename);
// 服务端 filename 可能含 app/ 前缀,提取纯相对路径
let expected_prefix = format!("{}/", app_name);
let relative_path = if filename.starts_with(&expected_prefix) {
&filename[expected_prefix.len()..]
} else {
filename
};
let key = format!("{}/{}", app_name, relative_path);
let mut app_map = ctx.app_download_map.lock().unwrap();
let temp_path = app_map.get(&key).and_then(|s| s.temp_path.clone());
if let Some(ref temp) = temp_path {
let app_data_dir = get_updater_data_dir().join(app_name);
// filename 可能含路径分隔符,取纯文件名部分以避免路径重复
let tmp_filename = std::path::Path::new(filename)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(filename);
let final_path = app_data_dir.join(tmp_filename);
// final_path 与 tmp 文件同一目录(升级目录),与 handle_app_file_chunk 的 is_last 块保持一致
let upgrade_dir = temp.parent().unwrap();
let final_path = upgrade_dir.join(tmp_filename);
let _ = fs::create_dir_all(final_path.parent().unwrap());
if let Err(e) = fs::rename(temp, &final_path) {
@@ -1905,31 +1923,30 @@ async fn run_updater(debug_mode: bool) -> bool {
// ========== 阶段3.5:处理应用 FileChunk ==========
if current_phase == UpdatePhase::AppsWaitAllFile && !filename.is_empty() {
let is_last = data_obj.get("is_last").and_then(|v| v.as_bool()).unwrap_or(false);
let chunk_offset = data_obj.get("offset").and_then(|v| v.as_u64()).unwrap_or(0);
let file_size = data_obj.get("file_size").and_then(|v| v.as_u64()).unwrap_or(0);
let chunk_size = 4096u64;
let candidates = ctx_clone.candidates.lock().unwrap().clone();
for (app_name, _) in &candidates {
// filename 可能含路径分隔符,取纯文件名部分来构造 tmp 路径
let tmp_filename = std::path::Path::new(filename)
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(filename);
let upgrade_path = get_updater_data_dir()
.join("Updater")
.join("UpGrade")
.join(app_name)
.join(format!("{}.tmp", tmp_filename));
if !upgrade_path.exists() {
continue;
}
let completed = handle_app_file_chunk(&ctx_clone, app_name, data_obj, debug_msg);
if let Some((app, file)) = completed {
if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [应用] {} / {} 下载完成", ts, app, file);
let result = handle_app_file_chunk(&ctx_clone, app_name, data_obj, debug_msg);
if let Some((_, relative_path)) = result {
if is_last {
// 文件下完,标记并请求下一个文件
if debug_msg {
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
println!("{} [应用] {} / {} 下载完成", ts, app_name, filename);
}
ctx_clone.app_completed_set.lock().unwrap().insert(format!("{}/{}", app_name, filename));
send_next_download(&ctx_clone, &sender);
} else {
// 非最后一块,立即请求下一块(用 relative_path不带 app_name/ 前缀)
let next_offset = chunk_offset + chunk_size;
if next_offset < file_size {
request_download_for_app(&sender, app_name, &relative_path, next_offset);
}
}
ctx_clone.app_completed_set.lock().unwrap().insert(format!("{}/{}", app, file));
// 一个文件下完,发下一个
send_next_download(&ctx_clone, &sender);
}
break;
}
@@ -1985,13 +2002,13 @@ async fn run_updater(debug_mode: bool) -> bool {
.file_name()
.and_then(|n| n.to_str())
.unwrap_or(filename);
let tmp_path = get_updater_data_dir()
let upgrade_dir = get_updater_data_dir()
.join("Updater")
.join("UpGrade")
.join(app_name)
.join(format!("{}.tmp", tmp_filename));
// rename 后的最终路径(与 handle_app_download_complete 内部保持一致)
let final_path = get_updater_data_dir().join(app_name).join(tmp_filename);
.join(app_name);
let tmp_path = upgrade_dir.join(format!("{}.tmp", tmp_filename));
// rename 后的最终路径(与 handle_app_file_chunk 的 is_last 块保持一致)
let final_path = upgrade_dir.join(tmp_filename);
// tmp 或 final 文件存在才处理
log_print!("{} [DownloadComplete] 检查路径: tmp={:?}, final={:?}, tmp_exists={}, final_exists={}",
ts, tmp_path, final_path, tmp_path.exists(), final_path.exists());