fix(desktop): rewrite upload with blocking reqwest, remove tokio dependency
This commit is contained in:
@@ -5,8 +5,7 @@ edition = "2021"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
eframe = "0.31"
|
eframe = "0.31"
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "multipart"] }
|
reqwest = { version = "0.12", default-features = false, features = ["rustls-tls", "multipart", "blocking"] }
|
||||||
tokio = { version = "1", features = ["rt", "macros"] }
|
|
||||||
serde = { version = "1", features = ["derive"] }
|
serde = { version = "1", features = ["derive"] }
|
||||||
serde_json = "1"
|
serde_json = "1"
|
||||||
|
|
||||||
|
|||||||
@@ -1,15 +1,11 @@
|
|||||||
#![windows_subsystem = "windows"]
|
#![windows_subsystem = "windows"]
|
||||||
|
|
||||||
use eframe::egui;
|
use eframe::egui;
|
||||||
use reqwest::multipart;
|
use reqwest::blocking::multipart;
|
||||||
use std::sync::mpsc;
|
use std::sync::mpsc;
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
struct UploadResponse {
|
struct UploadResponse {
|
||||||
#[allow(dead_code)]
|
|
||||||
id: Option<String>,
|
|
||||||
#[allow(dead_code)]
|
|
||||||
filename: Option<String>,
|
|
||||||
share_url: Option<String>,
|
share_url: Option<String>,
|
||||||
error: Option<String>,
|
error: Option<String>,
|
||||||
}
|
}
|
||||||
@@ -43,27 +39,9 @@ impl App {
|
|||||||
self.upload_rx = Some(rx);
|
self.upload_rx = Some(rx);
|
||||||
self.uploading = true;
|
self.uploading = true;
|
||||||
|
|
||||||
let filename = filepath
|
|
||||||
.file_name()
|
|
||||||
.unwrap_or_default()
|
|
||||||
.to_string_lossy()
|
|
||||||
.to_string();
|
|
||||||
|
|
||||||
std::thread::spawn(move || {
|
std::thread::spawn(move || {
|
||||||
let rt = tokio::runtime::Builder::new_current_thread()
|
let result = do_upload(&api_url, &filepath);
|
||||||
.enable_all()
|
let _ = tx.send(result);
|
||||||
.build();
|
|
||||||
match rt {
|
|
||||||
Ok(rt) => {
|
|
||||||
let result = rt.block_on(async {
|
|
||||||
do_upload(&api_url, &filepath, &filename).await
|
|
||||||
});
|
|
||||||
let _ = tx.send(result);
|
|
||||||
}
|
|
||||||
Err(e) => {
|
|
||||||
let _ = tx.send(UploadResult::Err(format!("启动运行时失败: {}", e)));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -87,55 +65,56 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn do_upload(api_url: &str, filepath: &std::path::Path, filename: &str) -> UploadResult {
|
fn do_upload(api_url: &str, filepath: &std::path::Path) -> UploadResult {
|
||||||
|
let filename = filepath
|
||||||
|
.file_name()
|
||||||
|
.map(|n| n.to_string_lossy().to_string())
|
||||||
|
.unwrap_or_else(|| "unknown".to_owned());
|
||||||
|
|
||||||
let data = match std::fs::read(filepath) {
|
let data = match std::fs::read(filepath) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(e) => return UploadResult::Err(format!("读取文件失败: {}", e)),
|
Err(e) => return UploadResult::Err(format!("读取文件失败: {}", e)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let part = multipart::Part::bytes(data)
|
let part = multipart::Part::bytes(data).file_name(filename);
|
||||||
.file_name(filename.to_owned())
|
|
||||||
.mime_str("application/octet-stream")
|
|
||||||
.unwrap_or_else(|_| multipart::Part::bytes(vec![]).file_name(filename.to_owned()));
|
|
||||||
|
|
||||||
let form = multipart::Form::new()
|
let form = multipart::Form::new()
|
||||||
.part("file", part)
|
.part("file", part)
|
||||||
.text("expiry", "24h");
|
.text("expiry", "24h");
|
||||||
|
|
||||||
let client = match reqwest::Client::builder().build() {
|
let client = match reqwest::blocking::Client::builder()
|
||||||
|
.user_agent("TempFileTransfer-Desktop/0.1")
|
||||||
|
.timeout(std::time::Duration::from_secs(300))
|
||||||
|
.build()
|
||||||
|
{
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err(e) => return UploadResult::Err(format!("创建客户端失败: {}", e)),
|
Err(e) => return UploadResult::Err(format!("创建客户端失败: {}", e)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let resp = match client.post(api_url).multipart(form).send().await {
|
let resp = match client.post(api_url).multipart(form).send() {
|
||||||
Ok(r) => r,
|
Ok(r) => r,
|
||||||
Err(e) => return UploadResult::Err(format!("请求失败: {}", e)),
|
Err(e) => return UploadResult::Err(format!("网络请求失败: {}", e)),
|
||||||
};
|
};
|
||||||
|
|
||||||
let status = resp.status();
|
let status = resp.status();
|
||||||
let body = match resp.text().await {
|
let body = match resp.text() {
|
||||||
Ok(b) => b,
|
Ok(b) => b,
|
||||||
Err(e) => return UploadResult::Err(format!("读取响应失败: {}", e)),
|
Err(e) => return UploadResult::Err(format!("读取响应失败: {}", e)),
|
||||||
};
|
};
|
||||||
|
|
||||||
if !status.is_success() {
|
if !status.is_success() {
|
||||||
let err: UploadResponse = serde_json::from_str(&body).unwrap_or(UploadResponse {
|
let err: UploadResponse = serde_json::from_str(&body).unwrap_or(UploadResponse {
|
||||||
id: None,
|
|
||||||
filename: None,
|
|
||||||
share_url: None,
|
share_url: None,
|
||||||
error: Some(body),
|
error: Some(body),
|
||||||
});
|
});
|
||||||
return UploadResult::Err(err.error.unwrap_or_else(|| "未知错误".to_owned()));
|
return UploadResult::Err(err.error.unwrap_or_else(|| format!("HTTP {}", status)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let result: UploadResponse = match serde_json::from_str(&body) {
|
match serde_json::from_str::<UploadResponse>(&body) {
|
||||||
Ok(r) => r,
|
Ok(r) => match r.share_url {
|
||||||
Err(e) => return UploadResult::Err(format!("解析响应失败: {}", e)),
|
Some(url) => UploadResult::Ok(url),
|
||||||
};
|
None => UploadResult::Err(r.error.unwrap_or_else(|| "未知服务器响应".to_owned())),
|
||||||
|
},
|
||||||
match result.share_url {
|
Err(e) => UploadResult::Err(format!("解析响应失败: {} - body: {}", e, body)),
|
||||||
Some(url) => UploadResult::Ok(url),
|
|
||||||
None => UploadResult::Err(result.error.unwrap_or_else(|| "未知服务器响应".to_owned())),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,13 +215,14 @@ impl eframe::App for App {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
ctx.request_repaint_after(std::time::Duration::from_millis(100));
|
ctx.request_repaint_after(std::time::Duration::from_millis(200));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load_chinese_font(ctx: &egui::Context) {
|
fn load_chinese_font(ctx: &egui::Context) {
|
||||||
let font_paths = [
|
let font_paths = [
|
||||||
"C:\\Windows\\Fonts\\msyh.ttc",
|
"C:\\Windows\\Fonts\\msyh.ttc",
|
||||||
|
"C:\\Windows\\Fonts\\msyhbd.ttc",
|
||||||
"C:\\Windows\\Fonts\\simhei.ttf",
|
"C:\\Windows\\Fonts\\simhei.ttf",
|
||||||
"C:\\Windows\\Fonts\\simsun.ttc",
|
"C:\\Windows\\Fonts\\simsun.ttc",
|
||||||
];
|
];
|
||||||
@@ -250,9 +230,10 @@ fn load_chinese_font(ctx: &egui::Context) {
|
|||||||
for path in &font_paths {
|
for path in &font_paths {
|
||||||
if let Ok(data) = std::fs::read(path) {
|
if let Ok(data) = std::fs::read(path) {
|
||||||
let mut fonts = egui::FontDefinitions::default();
|
let mut fonts = egui::FontDefinitions::default();
|
||||||
fonts
|
fonts.font_data.insert(
|
||||||
.font_data
|
"chinese".to_owned(),
|
||||||
.insert("chinese".to_owned(), std::sync::Arc::new(egui::FontData::from_owned(data)));
|
std::sync::Arc::new(egui::FontData::from_owned(data)),
|
||||||
|
);
|
||||||
fonts
|
fonts
|
||||||
.families
|
.families
|
||||||
.get_mut(&egui::FontFamily::Proportional)
|
.get_mut(&egui::FontFamily::Proportional)
|
||||||
@@ -262,7 +243,7 @@ fn load_chinese_font(ctx: &egui::Context) {
|
|||||||
.families
|
.families
|
||||||
.get_mut(&egui::FontFamily::Monospace)
|
.get_mut(&egui::FontFamily::Monospace)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.push("chinese".to_owned());
|
.insert(0, "chinese".to_owned());
|
||||||
ctx.set_fonts(fonts);
|
ctx.set_fonts(fonts);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user