feat: add icon file content check to prevent saving failed downloads

This commit is contained in:
wonfen 2025-03-09 02:34:57 +08:00
parent 18f86874ee
commit c1a9de4d66

View File

@ -90,42 +90,62 @@ pub async fn download_icon_cache(url: String, name: String) -> CmdResult<String>
let temp_path = icon_cache_dir.join(format!("{}.downloading", &name)); let temp_path = icon_cache_dir.join(format!("{}.downloading", &name));
// 下载文件到临时位置 // 下载文件到临时位置
let response = wrap_err!(reqwest::get(url).await)?; let response = wrap_err!(reqwest::get(&url).await)?;
// 检查内容类型是否为图片
let content_type = response.headers()
.get(reqwest::header::CONTENT_TYPE)
.and_then(|v| v.to_str().ok())
.unwrap_or("");
let is_image = content_type.starts_with("image/");
// 获取响应内容
let content = wrap_err!(response.bytes().await)?; let content = wrap_err!(response.bytes().await)?;
// 写入临时文件 // 检查内容是否为HTML (针对CDN错误页面)
{ let is_html = content.len() > 15 &&
let mut file = match std::fs::File::create(&temp_path) { (content.starts_with(b"<!DOCTYPE html") ||
Ok(file) => file, content.starts_with(b"<html") ||
Err(_) => { content.starts_with(b"<?xml"));
if icon_path.exists() {
return Ok(icon_path.to_string_lossy().to_string());
} else {
return Err("Failed to create temporary file".into());
}
}
};
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?;
}
// 再次检查目标文件是否已存在,避免重命名覆盖其他线程已完成的文件 // 只有当内容确实是图片时才保存
if !icon_path.exists() { if is_image && !is_html {
// 使用原子重命名操作将临时文件移动到最终位置 {
match std::fs::rename(&temp_path, &icon_path) { let mut file = match std::fs::File::create(&temp_path) {
Ok(_) => {}, Ok(file) => file,
Err(_) => { Err(_) => {
let _ = std::fs::remove_file(&temp_path); if icon_path.exists() {
if icon_path.exists() { return Ok(icon_path.to_string_lossy().to_string());
return Ok(icon_path.to_string_lossy().to_string()); } else {
return Err("Failed to create temporary file".into());
}
}
};
wrap_err!(std::io::copy(&mut content.as_ref(), &mut file))?;
}
// 再次检查目标文件是否已存在,避免重命名覆盖其他线程已完成的文件
if !icon_path.exists() {
match std::fs::rename(&temp_path, &icon_path) {
Ok(_) => {},
Err(_) => {
let _ = std::fs::remove_file(&temp_path);
if icon_path.exists() {
return Ok(icon_path.to_string_lossy().to_string());
}
} }
} }
} else {
let _ = std::fs::remove_file(&temp_path);
} }
Ok(icon_path.to_string_lossy().to_string())
} else { } else {
let _ = std::fs::remove_file(&temp_path); let _ = std::fs::remove_file(&temp_path);
Err(format!("下载的内容不是有效图片: {}", url).into())
} }
Ok(icon_path.to_string_lossy().to_string())
} }
#[derive(Debug, serde::Serialize, serde::Deserialize)] #[derive(Debug, serde::Serialize, serde::Deserialize)]