diff --git a/Windows/CS/Framework4.0/Updater/src/main.rs b/Windows/CS/Framework4.0/Updater/src/main.rs index d5c4869..b7e61a1 100644 --- a/Windows/CS/Framework4.0/Updater/src/main.rs +++ b/Windows/CS/Framework4.0/Updater/src/main.rs @@ -1210,8 +1210,8 @@ fn scan_upgrade_dir(app_name: &str) -> Vec<(String, PathBuf)> { files } -/// 扫描本地升级目录,返回 .tmp 文件列表(相对路径) -fn scan_local_tmp_files(app_name: &str) -> Vec { +/// 扫描本地升级目录,返回 .tmp 文件列表(正式文件名 + 大小) +fn scan_local_tmp_files(app_name: &str) -> Vec<(String, u64)> { let upgrade_base = get_updater_data_dir() .join("Updater") .join("UpGrade") @@ -1241,7 +1241,10 @@ fn scan_local_tmp_files(app_name: &str) -> Vec { // 去掉 .tmp 后缀,发送正式文件名 let official_name = rel_str.strip_suffix(".tmp") .unwrap_or(&rel_str); - tmp_files.push(official_name.to_string()); + // 获取文件大小 + if let Ok(metadata) = fs::metadata(&path) { + tmp_files.push((official_name.to_string(), metadata.len())); + } } } } @@ -1257,13 +1260,20 @@ fn scan_local_tmp_files(app_name: &str) -> Vec { fn send_get_all_file( sender: &cube_lib::websocket::MessageSender, app_name: &str, - tmp_files: &[String], + tmp_files: &[(String, u64)], ) { let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); - let tmp_files_json = serde_json::to_string(tmp_files).unwrap_or_else(|_| "[]".to_string()); + // 构造成 {filename, size} 格式的数组 + let tmp_files_json: Vec = tmp_files.iter() + .map(|(name, size)| serde_json::json!({ + "filename": name, + "size": size + })) + .collect(); + let tmp_files_str = serde_json::to_string(&tmp_files_json).unwrap_or_else(|_| "[]".to_string()); let msg_str = format!( r#"{{"Type":"GetAllFile","Data":{{"app_name":"{}","tmp_files":{}}}}}"#, - app_name, tmp_files_json + app_name, tmp_files_str ); log_print!("{} 发送消息:{}", ts, msg_str); sender.send(msg_str); @@ -1673,7 +1683,7 @@ async fn run_updater(debug_mode: bool) -> bool { .unwrap_or_default(); // 获取服务端临时文件列表 - let server_tmp_files: Vec<(String, Option, Option)> = allfile_data + let _server_tmp_files: Vec<(String, Option, Option)> = allfile_data .get("tmp_files") .and_then(|v| v.as_array()) .map(|arr| { @@ -1696,84 +1706,90 @@ async fn run_updater(debug_mode: bool) -> bool { .join("UpGrade") .join(app_name); - // 1. 扫描本地文件 + // 1. 扫描本地文件(分别记录正式文件和 tmp 文件) let mut local_files: std::collections::HashSet = std::collections::HashSet::new(); + #[allow(dead_code)] + let mut local_tmp_files: std::collections::HashSet = std::collections::HashSet::new(); if upgrade_base.exists() { if let Ok(entries) = fs::read_dir(&upgrade_base) { for entry in entries.flatten() { let path = entry.path(); if path.is_file() { if let Some(name) = path.file_name().and_then(|n| n.to_str()) { - local_files.insert(name.to_string()); + if name.ends_with(".tmp") { + // tmp 文件:去掉 .tmp 后缀存储 + let official_name = name.trim_end_matches(".tmp"); + local_tmp_files.insert(official_name.to_string()); + } else { + local_files.insert(name.to_string()); + } } } } } } - // 2. 以服务端为准,删除本地多余文件 - let server_filenames: std::collections::HashSet = server_files + // 2. 以服务端为准,删除本地多余的正式文件和临时文件 + let server_official_names: std::collections::HashSet = server_files .iter() .map(|(name, _, _)| name.clone()) - .chain(server_tmp_files.iter().map(|(name, _, _)| name.clone())) .collect(); for local_file in &local_files { - if !server_filenames.contains(local_file) { + if !server_official_names.contains(local_file) { + // 删除本地正式文件 let file_path = upgrade_base.join(local_file); if file_path.exists() { - log_print!("{} [AllFile] 删除多余文件: {}", ts, local_file); + log_print!("{} [AllFile] 删除多余正式文件: {}", ts, local_file); let _ = fs::remove_file(&file_path); } - } - } - - // 3. 处理临时文件(MD5 不一致则删除) - for (tmp_filename, _, server_md5) in &server_tmp_files { - let tmp_path = upgrade_base.join(tmp_filename); - if tmp_path.exists() { - if let Some(sm) = server_md5 { - // 计算本地 tmp 文件的 md5(全文件) - let local_md5 = compute_file_md5(&tmp_path); - if let Some(lm) = local_md5 { - if &lm != sm { - log_print!("{} [AllFile] tmp {} MD5不一致 (本地={}, 服务端={}),删除", ts, tmp_filename, lm, sm); - let _ = fs::remove_file(&tmp_path); - } else { - log_print!("{} [AllFile] tmp {} MD5一致,跳过", ts, tmp_filename); - } - } + // 同时删除对应的临时文件(如有) + let tmp_file_with_ext = format!("{}.tmp", local_file); + let tmp_path = upgrade_base.join(&tmp_file_with_ext); + if tmp_path.exists() { + log_print!("{} [AllFile] 删除多余临时文件: {}", ts, tmp_file_with_ext); + let _ = fs::remove_file(&tmp_path); } } } - // 4. 处理正式文件(MD5 不一致则创建空 tmp 文件) + // 3. 处理服务端文件列表:比较 MD5,决定如何处理 for (filename, _, server_md5) in &server_files { let file_path = upgrade_base.join(filename); let tmp_filename = format!("{}.tmp", filename); let tmp_path = upgrade_base.join(&tmp_filename); + let has_tmp = tmp_path.exists(); + let has_official = file_path.exists(); - // 检查是否已有有效的 tmp 文件 - if tmp_path.exists() { - // tmp 已存在,由后续 Md5 响应处理 - continue; - } - - // 检查正式文件是否存在 - if file_path.exists() { - // 计算本地文件的 md5 + if has_tmp { + // 情况A:本地有临时文件 → 比较临时文件 + let local_md5 = compute_file_md5(&tmp_path); + if let Some(lm) = local_md5 { + if &lm != server_md5 { + // MD5 不对 → 清空临时文件内容 + log_print!("{} [AllFile] tmp {} MD5不一致 (本地={}, 服务端={}),清空", ts, tmp_filename, lm, server_md5); + if let Ok(file) = fs::File::create(&tmp_path) { + let _ = file.set_len(0); + } + // 发送下载请求 + request_download_for_app(&sender, &app_name, filename, 0); + update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); + } else { + log_print!("{} [AllFile] tmp {} MD5一致,续传", ts, tmp_filename); + } + } + } else if has_official { + // 情况B:本地没有临时文件但有正式文件 → 比较正式文件 let local_md5 = compute_file_md5(&file_path); - if let Some(ref lm) = local_md5 { - if lm != server_md5 { + if let Some(lm) = local_md5 { + if &lm != server_md5 { + // MD5 不对 → 创建空的临时文件 log_print!("{} [AllFile] {} MD5不一致 (本地={}, 服务端={}),创建 tmp", ts, filename, lm, server_md5); - // 先创建文件的父目录 if let Some(parent) = tmp_path.parent() { let _ = fs::create_dir_all(parent); } - // 创建空 tmp 文件标记需要下载 if let Ok(_) = File::create(&tmp_path) { log_print!("{} [AllFile] 创建空 tmp 文件: {}", ts, tmp_filename); - // 为这个正式文件发送下载请求 request_download_for_app(&sender, &app_name, filename, 0); update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); } @@ -1782,35 +1798,28 @@ async fn run_updater(debug_mode: bool) -> bool { } } } else { - // 文件不存在,创建空 tmp + // 情况C:本地连正式文件都没有 → 直接创建空的临时文件 log_print!("{} [AllFile] {} 不存在,创建 tmp", ts, filename); - // 先创建文件的父目录 if let Some(parent) = tmp_path.parent() { let _ = fs::create_dir_all(parent); } if let Ok(_) = File::create(&tmp_path) { log_print!("{} [AllFile] 创建空 tmp 文件: {}", ts, tmp_filename); - // 为这个正式文件发送下载请求(函数内部会记录发送消息日志) request_download_for_app(&sender, &app_name, filename, 0); update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); } } } - // 5. 发送下载请求(处理服务端返回的 tmp 文件) - for (tmp_filename, _, _server_md5) in &server_tmp_files { - let tmp_path = upgrade_base.join(tmp_filename); - let original_filename = tmp_filename.trim_end_matches(".tmp"); - + // 4. 为剩余的有效临时文件发送续传请求 + for (filename, _, _) in &server_files { + let tmp_filename = format!("{}.tmp", filename); + let tmp_path = upgrade_base.join(&tmp_filename); if tmp_path.exists() { - // tmp 存在,发送请求续传 + // tmp 存在,发送续传请求 let file_size = fs::metadata(&tmp_path).map(|m| m.len()).unwrap_or(0); - request_download_for_app(&sender, &app_name, original_filename, file_size); - } else { - // tmp 不存在,重新下载 - request_download_for_app(&sender, &app_name, original_filename, 0); + request_download_for_app(&sender, &app_name, filename, file_size); } - update_performed_clone2.store(true, std::sync::atomic::Ordering::SeqCst); } // 6. 检查是否所有应用都处理完成