处理临时文件

This commit is contained in:
zqm
2026-04-09 17:25:23 +08:00
parent a439908abb
commit e79ee55d1e

View File

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