diff --git a/Windows/CS/Framework4.0/Updater/src/main.rs b/Windows/CS/Framework4.0/Updater/src/main.rs index b764a21..b6726e8 100644 --- a/Windows/CS/Framework4.0/Updater/src/main.rs +++ b/Windows/CS/Framework4.0/Updater/src/main.rs @@ -19,7 +19,7 @@ fn init_log_file() { Err(_) => return, }; let log_dir = match exe_path.parent() { - Some(d) => d.join("Log"), + Some(d) => d.join("Updater").join("Log"), None => return, }; @@ -143,6 +143,7 @@ use std::path::PathBuf; use std::pin::Pin; use std::process::Command; use std::io::Write as StdWrite; +use std::io::Seek; use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; use cube_lib::websocket::{WebSocketClient, WebSocketConfig}; @@ -597,9 +598,11 @@ fn handle_file_chunk( let mut ctx = ctx.download_state.lock().unwrap(); - // 如果是新文件或 offset=0,重置状态 + // 只有文件名变化才重置(开始下载新文件) + // 注意:不要用 offset==0 判断是否重置! + // offset>0 表示续传(server 从该偏移继续发块),不应重置 tmp 文件 let need_reset = match ctx.as_ref() { - Some(s) => &s.filename != &final_filename || offset == 0, + Some(s) => &s.filename != &final_filename, None => true, }; if need_reset { @@ -618,23 +621,46 @@ fn handle_file_chunk( let data_dir = get_updater_data_dir(); let temp_path = data_dir.join(format!("{}.tmp", final_filename)); - // 创建临时文件(截断) - match File::create(&temp_path) { - Ok(file) => { - *ctx = Some(DownloadState { - filename: final_filename.clone(), - offset: 0, - temp_path: Some(temp_path), - file: Some(file), - }); - } - Err(_) => { - if debug { - eprintln!("[下载] 无法创建临时文件: {}", filename); + // 续传:tmp 文件已存在,以追加方式打开(保留已有数据) + // 新下载:tmp 文件不存在,用 File::create 创建(截断模式) + let (file, resume_offset) = if temp_path.exists() { + use std::fs::OpenOptions; + match OpenOptions::new().write(true).append(true).open(&temp_path) { + Ok(f) => { + let current_size = f.metadata().map(|m| m.len()).unwrap_or(0); + if debug { + eprintln!("[下载] 续传:追加打开 {:?}, 当前大小={}", temp_path, current_size); + } + let mut file = f; + file.seek(std::io::SeekFrom::Start(current_size)).ok(); + (file, current_size) + } + Err(e) => { + if debug { + eprintln!("[下载] 无法追加打开临时文件 {}: {}", filename, e); + } + return None; } - return None; } - } + } else { + // 新下载,截断创建 + match File::create(&temp_path) { + Ok(file) => (file, 0u64), + Err(e) => { + if debug { + eprintln!("[下载] 无法创建临时文件 {}: {}", filename, e); + } + return None; + } + } + }; + + *ctx = Some(DownloadState { + filename: final_filename.clone(), + offset: resume_offset, + temp_path: Some(temp_path), + file: Some(file), + }); } // 解码并追加数据 @@ -1856,13 +1882,31 @@ async fn run_updater(debug_mode: bool) -> bool { let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); // 收到消息日志(始终记录) - let actual_data = data.get("Data").unwrap_or(&serde_json::Value::Null); - let data_str = serde_json::to_string(actual_data).unwrap_or_else(|_| "{}".to_string()); - log_print!("{} 收到消息:{{\"Type\":{},\"Data\":{}}}", - ts, - serde_json::to_string(&msg_type).unwrap_or_default(), - data_str - ); + // FileChunk 跳过完整 Data 序列化(Data 中含约40KB base64,同步阻塞tokio runtime导致30秒延迟) + if msg_type == "FileChunk" { + let filename = data.get("Data") + .and_then(|v| v.get("filename")) + .and_then(|v| v.as_str()) + .unwrap_or("?"); + let offset = data.get("Data") + .and_then(|v| v.get("offset")) + .and_then(|v| v.as_u64()) + .unwrap_or(0); + let is_last = data.get("Data") + .and_then(|v| v.get("is_last")) + .and_then(|v| v.as_bool()) + .unwrap_or(false); + log_print!("{} 收到消息:{{\"Type\":\"FileChunk\",\"filename\":\"{}\",\"offset\":{},\"is_last\":{}}}", + ts, filename, offset, is_last); + } else { + let actual_data = data.get("Data").unwrap_or(&serde_json::Value::Null); + let data_str = serde_json::to_string(actual_data).unwrap_or_else(|_| "{}".to_string()); + log_print!("{} 收到消息:{{\"Type\":{},\"Data\":{}}}", + ts, + serde_json::to_string(&msg_type).unwrap_or_default(), + data_str + ); + } // 收到 welcome 后,重置状态并从头开始执行流程 if msg_type == "welcome" { @@ -2379,11 +2423,23 @@ async fn run_updater(debug_mode: bool) -> bool { } break; } - } else { + } else { // ========== BootLoader/Updater 的 FileChunk 处理 ========== let completed = handle_file_chunk(&ctx_clone, data_obj, debug_msg); - // 只处理 Updater 下载完成(BootLoader 由 DownloadComplete 统一处理,避免重复) if let Some(ref completed_file) = completed { + // BootLoader 下载完成:推进到阶段2 + if completed_file == "BootLoader.exe" { + let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f"); + *ctx_clone.current_phase.lock().unwrap() = UpdatePhase::Updater; + log_print!("{} [阶段2] 检查 Updater...", ts); + let msg_str = format!( + r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#, + device_number + ); + log_print!("{} 发送消息:{}", ts, msg_str); + sender.send(msg_str); + } + // Updater 下载完成:启动 BootLoader if completed_file == "Updater.new.exe" { if !updater_downloaded_clone2.load(std::sync::atomic::Ordering::SeqCst) { updater_downloaded_clone2.store(true, std::sync::atomic::Ordering::SeqCst); @@ -2394,6 +2450,18 @@ async fn run_updater(debug_mode: bool) -> bool { schedule_bootloader_launch(debug_msg, shutdown_tx_arc_clone.clone()); } } + } else { + // 非最后一块:主动请求下一块(BootLoader/Updater 流式下载需客户端驱动) + 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; + if !is_last { + let next_offset = chunk_offset + chunk_size; + if next_offset < file_size { + request_download(&sender, filename, next_offset); + } + } } } }