升级成功后重启应用程序
This commit is contained in:
@@ -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(_) => {
|
||||
// 续传: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!("[下载] 无法创建临时文件: {}", filename);
|
||||
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;
|
||||
}
|
||||
}
|
||||
} 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,6 +1882,23 @@ async fn run_updater(debug_mode: bool) -> bool {
|
||||
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||
|
||||
// 收到消息日志(始终记录)
|
||||
// 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\":{}}}",
|
||||
@@ -1863,6 +1906,7 @@ async fn run_updater(debug_mode: bool) -> bool {
|
||||
serde_json::to_string(&msg_type).unwrap_or_default(),
|
||||
data_str
|
||||
);
|
||||
}
|
||||
|
||||
// 收到 welcome 后,重置状态并从头开始执行流程
|
||||
if msg_type == "welcome" {
|
||||
@@ -2382,8 +2426,20 @@ async fn run_updater(debug_mode: bool) -> bool {
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user