下载Updater
This commit is contained in:
@@ -2,7 +2,7 @@ extern crate winres;
|
|||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let mut res = winres::WindowsResource::new();
|
let mut res = winres::WindowsResource::new();
|
||||||
res.set("FileVersion", "0.0.0.1");
|
res.set("FileVersion", "0.0.0.2");
|
||||||
res.set("ProductVersion", "0.0.0.1");
|
res.set("ProductVersion", "0.0.0.1");
|
||||||
res.compile().unwrap();
|
res.compile().unwrap();
|
||||||
}
|
}
|
||||||
@@ -35,6 +35,11 @@ static DOWNLOAD_STATE: std::sync::Mutex<DownloadState> = std::sync::Mutex::new(D
|
|||||||
file: None,
|
file: None,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ===================== 下载流程状态 =====================
|
||||||
|
static BOOTLOADER_DOWNLOADED: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
|
||||||
|
static UPDATER_DOWNLOADED: std::sync::Mutex<bool> = std::sync::Mutex::new(false);
|
||||||
|
static SERVER_UPDATER_VERSION: std::sync::Mutex<Option<String>> = std::sync::Mutex::new(None);
|
||||||
|
|
||||||
// ===================== 版本比较与下载 =====================
|
// ===================== 版本比较与下载 =====================
|
||||||
/// 获取 Updater 数据目录(X:\AppData\,存放 BootLoader.exe 等)
|
/// 获取 Updater 数据目录(X:\AppData\,存放 BootLoader.exe 等)
|
||||||
fn get_updater_data_dir() -> PathBuf {
|
fn get_updater_data_dir() -> PathBuf {
|
||||||
@@ -97,26 +102,34 @@ fn get_local_file_version(filename: &str) -> String {
|
|||||||
|
|
||||||
/// 比较两个版本号(a < b 返回 true)
|
/// 比较两个版本号(a < b 返回 true)
|
||||||
fn version_less_than(a: &str, b: &str) -> bool {
|
fn version_less_than(a: &str, b: &str) -> bool {
|
||||||
|
// 按 '.' 分割版本号
|
||||||
let parse_v = |v: &str| -> Vec<u32> {
|
let parse_v = |v: &str| -> Vec<u32> {
|
||||||
v.split(|c: char| !c.is_ascii_digit() && c != '.')
|
v.split('.')
|
||||||
.filter(|s| !s.is_empty())
|
.filter(|s| !s.is_empty())
|
||||||
.filter_map(|s| s.parse().ok())
|
.filter_map(|s| s.parse().ok())
|
||||||
.collect()
|
.collect()
|
||||||
};
|
};
|
||||||
let a_parts: Vec<u32> = parse_v(a);
|
let a_parts: Vec<u32> = parse_v(a);
|
||||||
let b_parts: Vec<u32> = parse_v(b);
|
let b_parts: Vec<u32> = parse_v(b);
|
||||||
|
|
||||||
|
eprintln!("[DEBUG] version_less_than: a={:?} -> {:?}", a, a_parts);
|
||||||
|
eprintln!("[DEBUG] version_less_than: b={:?} -> {:?}", b, b_parts);
|
||||||
|
|
||||||
let max_len = a_parts.len().max(b_parts.len());
|
let max_len = a_parts.len().max(b_parts.len());
|
||||||
|
|
||||||
for i in 0..max_len {
|
for i in 0..max_len {
|
||||||
let av = if i < a_parts.len() { a_parts[i] } else { 0 };
|
let av = if i < a_parts.len() { a_parts[i] } else { 0 };
|
||||||
let bv = if i < b_parts.len() { b_parts[i] } else { 0 };
|
let bv = if i < b_parts.len() { b_parts[i] } else { 0 };
|
||||||
if av < bv {
|
if av < bv {
|
||||||
|
eprintln!("[DEBUG] version_less_than: {}[{}] < {}[{}] = true", a, i, b, i);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if av > bv {
|
if av > bv {
|
||||||
|
eprintln!("[DEBUG] version_less_than: {}[{}] > {}[{}] = false", a, i, b, i);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
eprintln!("[DEBUG] version_less_than: {} >= {} = false", a, b);
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -147,11 +160,163 @@ fn compute_file_hash(filename: &str, bytes: u64, _debug: bool) -> Option<String>
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 获取临时文件的当前大小(字节数)
|
/// 获取临时文件的当前大小(字节数)
|
||||||
|
/// 注意:Updater.exe 的临时文件是 Updater.new.exe.tmp
|
||||||
fn get_tmp_file_size(filename: &str) -> u64 {
|
fn get_tmp_file_size(filename: &str) -> u64 {
|
||||||
let tmp_path = get_updater_data_dir().join(format!("{}.tmp", filename));
|
let tmp_filename = if filename == "Updater.exe" { "Updater.new.exe.tmp" } else { filename };
|
||||||
|
let tmp_path = get_updater_data_dir().join(format!("{}.tmp", tmp_filename));
|
||||||
tmp_path.metadata().map(|m| m.len()).unwrap_or(0)
|
tmp_path.metadata().map(|m| m.len()).unwrap_or(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 检查是否存在 Updater.new.exe 文件
|
||||||
|
fn has_updater_new_exe() -> bool {
|
||||||
|
let updater_new_path = get_updater_data_dir().join("Updater.new.exe");
|
||||||
|
updater_new_path.exists()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 发送获取 Updater.exe 版本的请求
|
||||||
|
fn request_updater_version(sender: &cube_lib::websocket::MessageSender, device_number: &str, debug: bool) {
|
||||||
|
let msg_str = format!(
|
||||||
|
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["Updater.exe"]}}}}"#,
|
||||||
|
device_number
|
||||||
|
);
|
||||||
|
if debug {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [版本] 请求 Updater.exe 版本: {}", ts, msg_str);
|
||||||
|
}
|
||||||
|
sender.send(msg_str);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 检查并下载 Updater.exe
|
||||||
|
/// 当 BootLoader 下载完成后调用此函数
|
||||||
|
/// 返回 true 表示需要继续等待下载,false 表示流程结束
|
||||||
|
fn check_and_download_updater(sender: &cube_lib::websocket::MessageSender, device_number: &str, debug: bool) -> bool {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
|
||||||
|
// 获取服务端 Updater.exe 版本号
|
||||||
|
let server_version = SERVER_UPDATER_VERSION.lock().unwrap().clone();
|
||||||
|
|
||||||
|
if let Some(sv) = server_version {
|
||||||
|
let local_version = get_local_file_version("Updater.exe");
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
println!("{} [版本] Updater.exe: 服务端={}, 本地={}", ts, sv, local_version);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 比较版本:需要下载的条件
|
||||||
|
let need_update = local_version == "0.0.0" || version_less_than(&local_version, &sv);
|
||||||
|
|
||||||
|
if need_update {
|
||||||
|
// 需要下载 Updater.exe
|
||||||
|
let tmp_size = get_tmp_file_size("Updater.exe");
|
||||||
|
if tmp_size > 0 {
|
||||||
|
if debug {
|
||||||
|
println!("{} [续传] Updater.exe 发现未完成下载,请求 hash 校验...", ts);
|
||||||
|
}
|
||||||
|
request_file_md5(sender, "Updater.exe", tmp_size, debug);
|
||||||
|
} else {
|
||||||
|
if debug {
|
||||||
|
println!("{} [升级] Updater.exe 需要更新,开始下载...", ts);
|
||||||
|
}
|
||||||
|
request_download(sender, "Updater.exe", 0, debug);
|
||||||
|
}
|
||||||
|
true // 需要继续等待下载
|
||||||
|
} else {
|
||||||
|
// 版本已是最新,检查是否有 Updater.new.exe
|
||||||
|
if debug {
|
||||||
|
println!("{} [版本] Updater.exe 版本已是最新,无需下载", ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除可能存在的旧 Updater.new.exe
|
||||||
|
if has_updater_new_exe() {
|
||||||
|
let updater_new_path = get_updater_data_dir().join("Updater.new.exe");
|
||||||
|
let _ = fs::remove_file(&updater_new_path);
|
||||||
|
if debug {
|
||||||
|
println!("{} [清理] 删除旧的 Updater.new.exe", ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回 false 表示 Updater 不需要更新,调用者负责输出日志并退出
|
||||||
|
false
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有收到服务端 Updater 版本 → 重新请求 Updater 版本
|
||||||
|
if debug {
|
||||||
|
println!("{} [警告] 未收到 Updater.exe 版本信息,重新请求...", ts);
|
||||||
|
}
|
||||||
|
request_updater_version(sender, device_number, debug);
|
||||||
|
true // 需要继续等待版本响应
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 安排启动 BootLoader.exe(延迟执行,等待所有下载完成)
|
||||||
|
fn schedule_bootloader_launch(debug: bool) {
|
||||||
|
// 延迟 1 秒后启动,确保文件句柄已关闭
|
||||||
|
std::thread::spawn(move || {
|
||||||
|
std::thread::sleep(std::time::Duration::from_secs(1));
|
||||||
|
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
|
||||||
|
let bootloader_path = get_updater_data_dir().join("BootLoader.exe");
|
||||||
|
|
||||||
|
if !bootloader_path.exists() {
|
||||||
|
if debug {
|
||||||
|
println!("{} [错误] BootLoader.exe 不存在,无法启动", ts);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 启动 BootLoader.exe
|
||||||
|
if debug {
|
||||||
|
println!("{} [启动] 正在启动 BootLoader.exe...", ts);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(windows)]
|
||||||
|
{
|
||||||
|
use std::os::windows::process::CommandExt;
|
||||||
|
const DETACHED_PROCESS: u32 = 0x00000008;
|
||||||
|
const CREATE_NO_WINDOW: u32 = 0x08000000;
|
||||||
|
|
||||||
|
match Command::new(&bootloader_path)
|
||||||
|
.args(["--from-updater"])
|
||||||
|
.creation_flags(DETACHED_PROCESS | CREATE_NO_WINDOW)
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
if debug {
|
||||||
|
println!("{} [启动] BootLoader.exe 已启动,Updater 即将退出", ts);
|
||||||
|
}
|
||||||
|
// 给自己发送退出信号(优雅退出)
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, e);
|
||||||
|
// 仍然退出
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(windows))]
|
||||||
|
{
|
||||||
|
match Command::new(&bootloader_path)
|
||||||
|
.arg("--from-updater")
|
||||||
|
.spawn()
|
||||||
|
{
|
||||||
|
Ok(_) => {
|
||||||
|
if debug {
|
||||||
|
println!("{} [启动] BootLoader.exe 已启动,Updater 即将退出", ts);
|
||||||
|
}
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("{} [错误] 启动 BootLoader.exe 失败: {}", ts, e);
|
||||||
|
std::process::exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/// 发送 GetFileMd5 请求
|
/// 发送 GetFileMd5 请求
|
||||||
fn request_file_md5(sender: &cube_lib::websocket::MessageSender, filename: &str, bytes: u64, debug: bool) {
|
fn request_file_md5(sender: &cube_lib::websocket::MessageSender, filename: &str, bytes: u64, debug: bool) {
|
||||||
let msg_str = format!(
|
let msg_str = format!(
|
||||||
@@ -179,16 +344,19 @@ fn request_download(sender: &cube_lib::websocket::MessageSender, filename: &str,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 处理收到的文件块
|
/// 处理收到的文件块
|
||||||
fn handle_file_chunk(data: &serde_json::Map<std::string::String, serde_json::Value>, debug: bool) {
|
fn handle_file_chunk(data: &serde_json::Map<std::string::String, serde_json::Value>, debug: bool) -> Option<String> {
|
||||||
let filename = data.get("filename").and_then(|v| v.as_str()).unwrap_or("");
|
let filename = data.get("filename").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let offset = data.get("offset").and_then(|v| v.as_u64()).unwrap_or(0);
|
let offset = data.get("offset").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||||
let chunk_data = data.get("data").and_then(|v| v.as_str()).unwrap_or("");
|
let chunk_data = data.get("data").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let is_last = data.get("is_last").and_then(|v| v.as_bool()).unwrap_or(false);
|
let is_last = data.get("is_last").and_then(|v| v.as_bool()).unwrap_or(false);
|
||||||
|
|
||||||
|
// Updater.exe 保存为 Updater.new.exe
|
||||||
|
let final_filename = if filename == "Updater.exe" { "Updater.new.exe".to_string() } else { filename.to_string() };
|
||||||
|
|
||||||
let mut state = DOWNLOAD_STATE.lock().unwrap();
|
let mut state = DOWNLOAD_STATE.lock().unwrap();
|
||||||
|
|
||||||
// 如果是新文件或 offset=0,重置状态
|
// 如果是新文件或 offset=0,重置状态
|
||||||
if state.filename != filename || offset == 0 {
|
if state.filename != final_filename || offset == 0 {
|
||||||
// 关闭旧文件
|
// 关闭旧文件
|
||||||
if let Some(f) = state.file.take() {
|
if let Some(f) = state.file.take() {
|
||||||
drop(f);
|
drop(f);
|
||||||
@@ -200,12 +368,12 @@ fn handle_file_chunk(data: &serde_json::Map<std::string::String, serde_json::Val
|
|||||||
|
|
||||||
// 使用 AppData/Updater/ 作为数据目录
|
// 使用 AppData/Updater/ 作为数据目录
|
||||||
let data_dir = get_updater_data_dir();
|
let data_dir = get_updater_data_dir();
|
||||||
let temp_path = data_dir.join(format!("{}.tmp", filename));
|
let temp_path = data_dir.join(format!("{}.tmp", final_filename));
|
||||||
|
|
||||||
// 创建临时文件(截断)
|
// 创建临时文件(截断)
|
||||||
match File::create(&temp_path) {
|
match File::create(&temp_path) {
|
||||||
Ok(file) => {
|
Ok(file) => {
|
||||||
state.filename = filename.to_string();
|
state.filename = final_filename.clone();
|
||||||
state.offset = 0;
|
state.offset = 0;
|
||||||
state.temp_path = Some(temp_path);
|
state.temp_path = Some(temp_path);
|
||||||
state.file = Some(file);
|
state.file = Some(file);
|
||||||
@@ -214,7 +382,7 @@ fn handle_file_chunk(data: &serde_json::Map<std::string::String, serde_json::Val
|
|||||||
if debug {
|
if debug {
|
||||||
eprintln!("[下载] 无法创建临时文件: {}", filename);
|
eprintln!("[下载] 无法创建临时文件: {}", filename);
|
||||||
}
|
}
|
||||||
return;
|
return None;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -262,7 +430,7 @@ fn handle_file_chunk(data: &serde_json::Map<std::string::String, serde_json::Val
|
|||||||
|
|
||||||
if let Some(ref temp) = temp_path {
|
if let Some(ref temp) = temp_path {
|
||||||
let data_dir = get_updater_data_dir();
|
let data_dir = get_updater_data_dir();
|
||||||
let final_path = data_dir.join(filename);
|
let final_path = data_dir.join(&final_filename);
|
||||||
|
|
||||||
// 原子重命名
|
// 原子重命名
|
||||||
if let Err(e) = fs::rename(temp, &final_path) {
|
if let Err(e) = fs::rename(temp, &final_path) {
|
||||||
@@ -276,7 +444,7 @@ fn handle_file_chunk(data: &serde_json::Map<std::string::String, serde_json::Val
|
|||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
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");
|
||||||
println!("{} [下载] {} 下载完成,共 {} 字节", ts, filename, final_offset);
|
println!("{} [下载] {} 下载完成,共 {} 字节", ts, final_filename, final_offset);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -285,24 +453,64 @@ fn handle_file_chunk(data: &serde_json::Map<std::string::String, serde_json::Val
|
|||||||
state.offset = 0;
|
state.offset = 0;
|
||||||
state.temp_path = None;
|
state.temp_path = None;
|
||||||
state.file = None;
|
state.file = None;
|
||||||
|
|
||||||
|
// 返回下载完成的文件名,用于后续流程判断
|
||||||
|
Some(final_filename)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 处理下载完成消息
|
/// 处理下载完成消息
|
||||||
fn handle_download_complete(data: &serde_json::Map<std::string::String, serde_json::Value>, debug: bool) {
|
fn handle_download_complete(data: &serde_json::Map<std::string::String, serde_json::Value>, debug: bool) -> Option<String> {
|
||||||
let filename = data.get("filename").and_then(|v| v.as_str()).unwrap_or("");
|
let filename = data.get("filename").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let size = data.get("size").and_then(|v| v.as_u64()).unwrap_or(0);
|
let size = data.get("size").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||||
|
|
||||||
|
// Updater.exe 保存为 Updater.new.exe
|
||||||
|
let final_filename = if filename == "Updater.exe" { "Updater.new.exe".to_string() } else { filename.to_string() };
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
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");
|
||||||
println!("{} [下载] {} 下载完成,最终大小: {} 字节", ts, filename, size);
|
println!("{} [下载] {} 下载完成,最终大小: {} 字节", ts, final_filename, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut state = DOWNLOAD_STATE.lock().unwrap();
|
let mut state = DOWNLOAD_STATE.lock().unwrap();
|
||||||
|
|
||||||
|
// 如果有临时文件,执行重命名(防止 DownloadComplete 先于最后一个 FileChunk 到达)
|
||||||
|
// 先克隆需要的数据
|
||||||
|
let temp_to_rename = state.temp_path.clone();
|
||||||
|
let has_temp = temp_to_rename.is_some();
|
||||||
|
|
||||||
|
if has_temp {
|
||||||
|
let data_dir = get_updater_data_dir();
|
||||||
|
let final_path = data_dir.join(&final_filename);
|
||||||
|
|
||||||
|
// 先关闭文件句柄并获取 temp_path
|
||||||
|
let temp_path_owned = state.temp_path.take().unwrap();
|
||||||
state.filename = String::new();
|
state.filename = String::new();
|
||||||
state.offset = 0;
|
state.offset = 0;
|
||||||
state.temp_path = None;
|
|
||||||
state.file = None;
|
// 原子重命名
|
||||||
|
if let Err(e) = fs::rename(&temp_path_owned, &final_path) {
|
||||||
|
if debug {
|
||||||
|
eprintln!("[下载] 重命名失败: {}", e);
|
||||||
|
}
|
||||||
|
// 尝试直接覆盖写入
|
||||||
|
let _ = fs::copy(&temp_path_owned, &final_path);
|
||||||
|
let _ = fs::remove_file(&temp_path_owned);
|
||||||
|
}
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [下载] {} 临时文件已重命名", ts, final_filename);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 没有临时文件,只重置状态
|
||||||
|
state.filename = String::new();
|
||||||
|
state.offset = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Some(final_filename)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Updater 自身配置(AppData/Updater/config.json)
|
/// Updater 自身配置(AppData/Updater/config.json)
|
||||||
@@ -465,11 +673,11 @@ async fn run_updater(debug_mode: bool) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 收到 welcome 后,发送 GetFileVer
|
// 收到 welcome 后,发送 GetFileVer(同时请求 BootLoader 和 Updater)
|
||||||
if msg_type == "welcome" {
|
if msg_type == "welcome" {
|
||||||
if !device_number.is_empty() && device_number != "UNKNOWN" {
|
if !device_number.is_empty() && device_number != "UNKNOWN" {
|
||||||
let msg_str = format!(
|
let msg_str = format!(
|
||||||
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["BootLoader.exe"]}}}}"#,
|
r#"{{"Type":"GetFileVer","Data":{{"DeviceNumber":"{}","file_list":["BootLoader.exe","Updater.exe"]}}}}"#,
|
||||||
device_number
|
device_number
|
||||||
);
|
);
|
||||||
if debug_msg {
|
if debug_msg {
|
||||||
@@ -483,6 +691,16 @@ async fn run_updater(debug_mode: bool) {
|
|||||||
// 处理 FileVer 响应
|
// 处理 FileVer 响应
|
||||||
if msg_type == "FileVer" {
|
if msg_type == "FileVer" {
|
||||||
if let Some(file_versions) = data.get("Data").and_then(|d| d.get("file_versions")).and_then(|v| v.as_object()) {
|
if let Some(file_versions) = data.get("Data").and_then(|d| d.get("file_versions")).and_then(|v| v.as_object()) {
|
||||||
|
// 第一步:先遍历保存所有版本号
|
||||||
|
for (filename, server_ver) in file_versions {
|
||||||
|
let server_version = server_ver.as_str().unwrap_or("0.0.0");
|
||||||
|
// 保存 Updater.exe 的服务端版本号
|
||||||
|
if filename == "Updater.exe" {
|
||||||
|
*SERVER_UPDATER_VERSION.lock().unwrap() = Some(server_version.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 第二步:再遍历处理每个文件
|
||||||
for (filename, server_ver) in file_versions {
|
for (filename, server_ver) in file_versions {
|
||||||
let server_version = server_ver.as_str().unwrap_or("0.0.0");
|
let server_version = server_ver.as_str().unwrap_or("0.0.0");
|
||||||
let local_version = get_local_file_version(filename);
|
let local_version = get_local_file_version(filename);
|
||||||
@@ -512,6 +730,55 @@ async fn run_updater(debug_mode: bool) {
|
|||||||
}
|
}
|
||||||
request_download(&sender, filename, 0, debug_msg);
|
request_download(&sender, filename, 0, debug_msg);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
// 不需要更新
|
||||||
|
if debug_msg {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [版本] {} 版本已是最新,无需更新", ts, filename);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 对于 Updater.exe,检查是否有 Updater.new.exe
|
||||||
|
if filename == "Updater.exe" {
|
||||||
|
if has_updater_new_exe() {
|
||||||
|
// 删除旧版 Updater.new.exe(因为会重新下载)
|
||||||
|
let updater_new_path = get_updater_data_dir().join("Updater.new.exe");
|
||||||
|
let _ = fs::remove_file(&updater_new_path);
|
||||||
|
if debug_msg {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [清理] 删除旧的 Updater.new.exe", ts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标记 BootLoader 已"下载完成"(无需更新)
|
||||||
|
if filename == "BootLoader.exe" {
|
||||||
|
*BOOTLOADER_DOWNLOADED.lock().unwrap() = true;
|
||||||
|
if debug_msg {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [升级] BootLoader.exe 版本已是最新", ts);
|
||||||
|
}
|
||||||
|
// BootLoader 无需下载,检查 Updater
|
||||||
|
if !check_and_download_updater(&sender, &device_number, debug_msg) {
|
||||||
|
// Updater 也不需要更新 → 检查是否有正在下载的临时文件
|
||||||
|
let tmp_size = get_tmp_file_size("Updater.exe");
|
||||||
|
if tmp_size > 0 {
|
||||||
|
// 有正在下载的 Updater.tmp,等待下载完成
|
||||||
|
if debug_msg {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [升级] 发现未完成下载,等待 Updater.exe 下载完成...", ts);
|
||||||
|
}
|
||||||
|
// 请求 hash 校验,继续等待
|
||||||
|
request_file_md5(&sender, "Updater.exe", tmp_size, debug_msg);
|
||||||
|
} else {
|
||||||
|
// 真的不需要更新,也没有临时文件 → 退出
|
||||||
|
if debug_msg {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [升级] 所有文件已是最新版本,Updater 退出", ts);
|
||||||
|
}
|
||||||
|
std::process::exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -523,7 +790,12 @@ async fn run_updater(debug_mode: bool) {
|
|||||||
let filename = md5_data.get("filename").and_then(|v| v.as_str()).unwrap_or("");
|
let filename = md5_data.get("filename").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let server_md5 = md5_data.get("md5").and_then(|v| v.as_str()).unwrap_or("");
|
let server_md5 = md5_data.get("md5").and_then(|v| v.as_str()).unwrap_or("");
|
||||||
let bytes = md5_data.get("bytes").and_then(|v| v.as_u64()).unwrap_or(0);
|
let bytes = md5_data.get("bytes").and_then(|v| v.as_u64()).unwrap_or(0);
|
||||||
let tmp_filename = format!("{}.tmp", filename);
|
// Updater.exe 的临时文件名是 Updater.new.exe.tmp
|
||||||
|
let tmp_filename = if filename == "Updater.exe" {
|
||||||
|
"Updater.new.exe.tmp".to_string()
|
||||||
|
} else {
|
||||||
|
format!("{}.tmp", filename)
|
||||||
|
};
|
||||||
|
|
||||||
// 计算本地 tmp 文件前 bytes 字节的 md5
|
// 计算本地 tmp 文件前 bytes 字节的 md5
|
||||||
let local_md5 = compute_file_hash(&tmp_filename, bytes, debug_msg);
|
let local_md5 = compute_file_hash(&tmp_filename, bytes, debug_msg);
|
||||||
@@ -561,14 +833,55 @@ async fn run_updater(debug_mode: bool) {
|
|||||||
// 处理文件块
|
// 处理文件块
|
||||||
if msg_type == "FileChunk" {
|
if msg_type == "FileChunk" {
|
||||||
if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) {
|
if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) {
|
||||||
handle_file_chunk(data_obj, debug_msg);
|
let completed = handle_file_chunk(data_obj, debug_msg);
|
||||||
|
// 只处理 Updater 下载完成(BootLoader 由 DownloadComplete 统一处理,避免重复)
|
||||||
|
if let Some(ref completed_file) = completed {
|
||||||
|
if completed_file == "Updater.new.exe" {
|
||||||
|
let already_done = *UPDATER_DOWNLOADED.lock().unwrap();
|
||||||
|
if !already_done {
|
||||||
|
*UPDATER_DOWNLOADED.lock().unwrap() = true;
|
||||||
|
if debug_msg {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [升级] Updater.new.exe 下载完成(FileChunk),准备启动 BootLoader...", ts);
|
||||||
|
}
|
||||||
|
schedule_bootloader_launch(debug_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 处理下载完成
|
// 处理下载完成(统一处理,避免 FileChunk 和 DownloadComplete 重复处理)
|
||||||
if msg_type == "DownloadComplete" {
|
if msg_type == "DownloadComplete" {
|
||||||
if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) {
|
if let Some(data_obj) = data.get("Data").and_then(|v| v.as_object()) {
|
||||||
handle_download_complete(data_obj, debug_msg);
|
let completed = handle_download_complete(data_obj, debug_msg);
|
||||||
|
if let Some(ref completed_file) = completed {
|
||||||
|
// BootLoader.exe 下载完成
|
||||||
|
if completed_file == "BootLoader.exe" {
|
||||||
|
let already_started = *BOOTLOADER_DOWNLOADED.lock().unwrap();
|
||||||
|
if !already_started {
|
||||||
|
*BOOTLOADER_DOWNLOADED.lock().unwrap() = true;
|
||||||
|
if debug_msg {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [升级] BootLoader.exe 下载完成,开始下载 Updater.exe...", ts);
|
||||||
|
}
|
||||||
|
// 开始下载 Updater.exe
|
||||||
|
request_download(&sender, "Updater.exe", 0, debug_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Updater.new.exe 下载完成
|
||||||
|
else if completed_file == "Updater.new.exe" {
|
||||||
|
let already_done = *UPDATER_DOWNLOADED.lock().unwrap();
|
||||||
|
if !already_done {
|
||||||
|
*UPDATER_DOWNLOADED.lock().unwrap() = true;
|
||||||
|
if debug_msg {
|
||||||
|
let ts = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f");
|
||||||
|
println!("{} [升级] Updater.new.exe 下载完成(DownloadComplete),准备启动 BootLoader...", ts);
|
||||||
|
}
|
||||||
|
schedule_bootloader_launch(debug_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user