feat(desktop): add drag-and-drop upload for file view
This commit is contained in:
@@ -595,6 +595,82 @@ async fn api_native_download(
|
||||
})
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn api_upload_file(
|
||||
state: tauri::State<'_, ApiState>,
|
||||
base_url: String,
|
||||
file_path: String,
|
||||
target_path: String,
|
||||
) -> Result<BridgeResponse, String> {
|
||||
let trimmed_path = file_path.trim().to_string();
|
||||
if trimmed_path.is_empty() {
|
||||
return Err("上传文件路径不能为空".to_string());
|
||||
}
|
||||
|
||||
let source_path = PathBuf::from(trimmed_path);
|
||||
if !source_path.exists() {
|
||||
return Err("上传文件不存在".to_string());
|
||||
}
|
||||
if !source_path.is_file() {
|
||||
return Err("仅支持上传文件,不支持文件夹".to_string());
|
||||
}
|
||||
|
||||
let file_name = source_path
|
||||
.file_name()
|
||||
.and_then(|name| name.to_str())
|
||||
.map(|name| name.to_string())
|
||||
.ok_or_else(|| "无法识别文件名".to_string())?;
|
||||
|
||||
let file_bytes = fs::read(&source_path).map_err(|err| format!("读取文件失败: {}", err))?;
|
||||
let normalized_target = if target_path.trim().is_empty() {
|
||||
"/".to_string()
|
||||
} else {
|
||||
target_path
|
||||
};
|
||||
|
||||
let csrf_token = fetch_csrf_token(&state.client, &base_url).await?;
|
||||
let upload_url = join_api_url(&base_url, "/api/upload");
|
||||
if upload_url.trim().is_empty() {
|
||||
return Err("API 地址不能为空".to_string());
|
||||
}
|
||||
|
||||
let multipart = reqwest::multipart::Form::new()
|
||||
.text("path", normalized_target)
|
||||
.part("file", reqwest::multipart::Part::bytes(file_bytes).file_name(file_name));
|
||||
|
||||
let mut request = state
|
||||
.client
|
||||
.post(&upload_url)
|
||||
.header("Accept", "application/json")
|
||||
.timeout(Duration::from_secs(60 * 30))
|
||||
.multipart(multipart);
|
||||
|
||||
if let Some(csrf) = csrf_token {
|
||||
request = request.header("X-CSRF-Token", csrf);
|
||||
}
|
||||
|
||||
let response = request
|
||||
.send()
|
||||
.await
|
||||
.map_err(|err| format!("上传请求失败: {}", err))?;
|
||||
|
||||
let status = response.status();
|
||||
let text = response
|
||||
.text()
|
||||
.await
|
||||
.map_err(|err| format!("读取响应失败: {}", err))?;
|
||||
let data = match serde_json::from_str::<Value>(&text) {
|
||||
Ok(parsed) => parsed,
|
||||
Err(_) => fallback_json(status, &text),
|
||||
};
|
||||
|
||||
Ok(BridgeResponse {
|
||||
ok: status.is_success(),
|
||||
status: status.as_u16(),
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg_attr(mobile, tauri::mobile_entry_point)]
|
||||
pub fn run() {
|
||||
let client = reqwest::Client::builder()
|
||||
@@ -620,7 +696,8 @@ pub fn run() {
|
||||
api_create_share,
|
||||
api_delete_share,
|
||||
api_create_direct_link,
|
||||
api_native_download
|
||||
api_native_download,
|
||||
api_upload_file
|
||||
])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
|
||||
Reference in New Issue
Block a user