clean c advanced

This commit is contained in:
Julian Freeman
2026-03-01 19:38:31 -04:00
parent ebef785f9a
commit a170e2a4bd
3 changed files with 280 additions and 199 deletions

View File

@@ -4,11 +4,13 @@ use std::time::{SystemTime, Duration};
use serde::Serialize;
use std::collections::HashMap;
use std::sync::Mutex;
use std::process::Command;
use std::os::windows::process::CommandExt;
// 存储全盘扫描后的结果,以便前端按需查询
// 存储全盘扫描后的结果
pub struct DiskState {
pub dir_sizes: Mutex<HashMap<String, u64>>,
pub file_info: Mutex<HashMap<String, (u64, u32)>>, // 路径 -> (文件总大小, 文件数量)
pub file_info: Mutex<HashMap<String, (u64, u32)>>,
}
#[derive(Serialize, Clone)]
@@ -18,8 +20,8 @@ pub struct FileTreeNode {
pub is_dir: bool,
pub size: u64,
pub size_str: String,
pub percent: f32, // 占父目录的百分比
pub file_count: u32, // 仅对 "X个文件" 节点有效
pub percent: f32,
pub file_count: u32,
pub has_children: bool,
}
@@ -33,6 +35,69 @@ pub fn format_size(size: u64) -> String {
else { format!("{} B", size) }
}
// --- 高级清理功能实现 ---
// 1. 系统组件清理 (DISM)
pub async fn run_dism_cleanup() -> Result<String, String> {
const CREATE_NO_WINDOW: u32 = 0x08000000;
let output = Command::new("dism.exe")
.args(&["/online", "/Cleanup-Image", "/StartComponentCleanup"])
.creation_flags(CREATE_NO_WINDOW)
.output()
.map_err(|e| e.to_string())?;
if output.status.success() {
Ok("系统组件清理完成。".into())
} else {
Err(String::from_utf8_lossy(&output.stderr).to_string())
}
}
// 2. 清理缩略图缓存
pub async fn clean_thumbnails() -> Result<String, String> {
let local_app_data = std::env::var("LOCALAPPDATA").map_err(|_| "无法获取 LocalAppData 路径")?;
let thumb_path = Path::new(&local_app_data).join("Microsoft\\Windows\\Explorer");
let mut count = 0;
if thumb_path.exists() {
if let Ok(entries) = fs::read_dir(thumb_path) {
for entry in entries.filter_map(|e| e.ok()) {
let name = entry.file_name().to_string_lossy().to_lowercase();
if name.starts_with("thumbcache_") && name.ends_with(".db") {
// 缩略图文件通常被 Explorer 占用,这里尝试删除,失败也继续
if fs::remove_file(entry.path()).is_ok() {
count += 1;
}
}
}
}
}
if count > 0 {
Ok(format!("成功清理 {} 个缩略图缓存文件。", count))
} else {
Ok("未发现可清理的缩略图缓存,或文件正被系统占用。".into())
}
}
// 3. 关闭休眠文件
pub async fn disable_hibernation() -> Result<String, String> {
const CREATE_NO_WINDOW: u32 = 0x08000000;
let output = Command::new("powercfg.exe")
.args(&["-h", "off"])
.creation_flags(CREATE_NO_WINDOW)
.output()
.map_err(|e| e.to_string())?;
if output.status.success() {
Ok("休眠模式已关闭hiberfil.sys 已移除。".into())
} else {
Err("执行失败,请确保以管理员身份运行。".into())
}
}
// --- 原有逻辑保持 (磁盘树等) ---
pub async fn run_full_scan(root_path: String, state: &DiskState) {
use jwalk::WalkDir;
let mut dir_sizes = HashMap::new();
@@ -42,41 +107,30 @@ pub async fn run_full_scan(root_path: String, state: &DiskState) {
for entry in WalkDir::new(root).skip_hidden(false).into_iter().filter_map(|e| e.ok()) {
if entry.file_type.is_file() {
let size = entry.metadata().map(|m| m.len()).unwrap_or(0);
// 统计文件信息(归属于直接父目录)
if let Some(parent) = entry.parent_path().to_str() {
let info = file_info.entry(parent.to_string()).or_insert((0, 0));
info.0 += size;
info.1 += 1;
info.0 += size; info.1 += 1;
}
// 递归向上累加目录大小
let mut current_path = entry.parent_path().to_path_buf();
while current_path.starts_with(root) {
let path_str = current_path.to_string_lossy().to_string();
*dir_sizes.entry(path_str).or_insert(0) += size;
if current_path == root { break; }
if let Some(parent) = current_path.parent() {
current_path = parent.to_path_buf();
} else { break; }
if let Some(parent) = current_path.parent() { current_path = parent.to_path_buf(); } else { break; }
}
}
}
let mut state_dirs = state.dir_sizes.lock().unwrap();
let mut state_files = state.file_info.lock().unwrap();
*state_dirs = dir_sizes;
*state_files = file_info;
*state_dirs = dir_sizes; *state_files = file_info;
}
pub fn get_children(parent_path: String, state: &DiskState) -> Vec<FileTreeNode> {
let dir_sizes = state.dir_sizes.lock().unwrap();
let file_info = state.file_info.lock().unwrap();
let mut results = Vec::new();
let parent_size = *dir_sizes.get(&parent_path).unwrap_or(&1);
// 1. 获取子文件夹
if let Ok(entries) = fs::read_dir(Path::new(&parent_path)) {
for entry in entries.filter_map(|e| e.ok()) {
if entry.file_type().map(|t| t.is_dir()).unwrap_or(false) {
@@ -84,40 +138,29 @@ pub fn get_children(parent_path: String, state: &DiskState) -> Vec<FileTreeNode>
if let Some(&size) = dir_sizes.get(&path_str) {
results.push(FileTreeNode {
name: entry.file_name().to_string_lossy().to_string(),
path: path_str,
is_dir: true,
size,
size_str: format_size(size),
path: path_str, is_dir: true, size, size_str: format_size(size),
percent: (size as f64 / parent_size as f64 * 100.0) as f32,
file_count: 0,
has_children: true, // 简化处理,假设都有子项
file_count: 0, has_children: true,
});
}
}
}
}
// 2. 获取该目录下的合并文件项
if let Some(&(size, count)) = file_info.get(&parent_path) {
if count > 0 {
results.push(FileTreeNode {
name: format!("[{} 个文件]", count),
path: format!("{}\\__files__", parent_path),
is_dir: false,
size,
size_str: format_size(size),
is_dir: false, size, size_str: format_size(size),
percent: (size as f64 / parent_size as f64 * 100.0) as f32,
file_count: count,
has_children: false,
file_count: count, has_children: false,
});
}
}
results.sort_by(|a, b| b.size.cmp(&a.size));
results
}
// 快速扫描逻辑 (保持不变)
#[derive(Serialize, Clone)]
pub struct ScanItem { pub name: String, pub path: String, pub size: u64, pub count: u32 }
#[derive(Serialize)]
@@ -127,19 +170,15 @@ pub async fn run_fast_scan() -> FastScanResult {
let mut items = Vec::new();
let mut total_bytes = 0;
let mut total_count = 0;
let mut add_item = |name: &str, path: &str, filter: Option<u64>| {
let (size, count) = get_dir_stats(Path::new(path), filter);
items.push(ScanItem { name: name.into(), path: path.into(), size, count });
total_bytes += size;
total_count += count;
total_bytes += size; total_count += count;
};
if let Ok(t) = std::env::var("TEMP") { add_item("用户临时文件", &t, None); }
add_item("系统临时文件", "C:\\Windows\\Temp", None);
add_item("Windows 更新残留", "C:\\Windows\\SoftwareDistribution\\Download", Some(10));
add_item("传递优化缓存", "C:\\Windows\\ServiceProfiles\\NetworkService\\AppData\\Local\\Microsoft\\Windows\\DeliveryOptimization", None);
FastScanResult { items, total_size: format_size(total_bytes), total_count }
}
@@ -162,7 +201,6 @@ fn get_dir_stats(path: &Path, filter_days: Option<u64>) -> (u64, u32) {
(size, count)
}
pub async fn run_fast_clean(is_simulation: bool) -> Result<String, String> {
// 简化版,复用之前的逻辑
Ok("清理完成".into())
pub async fn run_fast_clean(_is_simulation: bool) -> Result<String, String> {
Ok("快速清理任务已成功模拟执行。".into())
}

View File

@@ -24,6 +24,23 @@ async fn get_tree_children(path: String, state: State<'_, cleaner::DiskState>) -
Ok(cleaner::get_children(path, &state))
}
// --- 高级清理命令 ---
#[tauri::command]
async fn clean_system_components() -> Result<String, String> {
cleaner::run_dism_cleanup().await
}
#[tauri::command]
async fn clean_thumbnails() -> Result<String, String> {
cleaner::clean_thumbnails().await
}
#[tauri::command]
async fn disable_hibernation() -> Result<String, String> {
cleaner::disable_hibernation().await
}
#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
tauri::Builder::default()
@@ -36,7 +53,10 @@ pub fn run() {
start_fast_scan,
start_fast_clean,
start_full_disk_scan,
get_tree_children
get_tree_children,
clean_system_components,
clean_thumbnails,
disable_hibernation
])
.run(tauri::generate_context!())
.expect("error while running tauri application");