处理临时文件

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
}
/// 扫描本地升级目录,返回 .tmp 文件列表(相对路径
fn scan_local_tmp_files(app_name: &str) -> Vec<String> {
/// 扫描本地升级目录,返回 .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<String> {
// 去掉 .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<String> {
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<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!(
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<u64>, Option<String>)> = allfile_data
let _server_tmp_files: Vec<(String, Option<u64>, Option<String>)> = 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<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 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()) {
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<String> = server_files
// 2. 以服务端为准,删除本地多余的正式文件和临时文件
let server_official_names: std::collections::HashSet<String> = 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);
// 同时删除对应的临时文件(如有)
let tmp_file_with_ext = format!("{}.tmp", local_file);
let tmp_path = upgrade_base.join(&tmp_file_with_ext);
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);
log_print!("{} [AllFile] 删除多余临时文件: {}", ts, tmp_file_with_ext);
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 {
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 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);
}
// 检查正式文件是否存在
if file_path.exists() {
// 计算本地文件的 md5
// 发送下载请求
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. 检查是否所有应用都处理完成