1.需求
我相信不少人知道一款Windows上的磁盘空间可视化神器SpaceSniffer
在磁盘空间不足的时候,这个工具能轻松地帮你找出磁盘中的大文件夹,来清理用不到的文件。不过这个工具也存在一个问题,就是仅支持windows平台。正好我自己的mac空间满了,于是就想着开发一个跨平台的磁盘空间可视化工具。
2.技术选择
我选择了以下技术进行开发
- Rust 代码性能高
- Tauri Electron打包太大,Tauri打出来的包体积可能只有几分之一。
- Vue 前端界面
- ECharts 磁盘空间可视化信息展示
3.开发
ECharts的磁盘可视化需要的json信息为:
于是我创建了一个struct用于传递给前端
#[derive(Debug, Serialize)]
pub struct FileNodeForJs {
pub name: String,
pub path: String,
pub value: u64,
pub children: Vec<FileNodeForJs>,
}
磁盘空间可视化最简单的办法是递归,这里用一段伪代码来简单描述获取文件大小的原理
function calculate_folder_size(folder):
total_size = 0
for item in folder:
if item is a file:
total_size += size_of(item)
else if item is a folder:
total_size += calculate_folder_size(item)
return total_size
但是实际上用这种方法开发会有一个明显的问题,就是只有当整个文件夹下所有内容都被扫描完成了,才会返回结果。那么我们如何实现SpaceSniffer一样的实时展示扫描结果呢?当然是传入一个静态变量啦!
#[derive(Debug, Clone)]
pub struct FileNode {
pub name: String,
pub path: String,
pub value: u64,
pub children: Vec<Arc<RwLock<FileNode>>>,
}
然后使用lazy_static!宏命令创建一个静态变量
lazy_static! {
static ref ROOT_NODE: Arc<RwLock<FileNode>> = Arc::new(RwLock::new(FileNode::empty()));
}
然后我们就可以扫描文件夹内容的时候每次修改静态变量,查询的时候也查询这个静态变量即可。
核心代码如下:
fn explore_directory(dir: &str, current_node: Arc<RwLock<FileNode>>, depth: usize) -> u64 {
if SHOULD_STOP.load(Ordering::SeqCst) {
return 0;
}
let mut total_size = 0u64;
if let Ok(entries) = std::fs::read_dir(dir) {
for entry in entries {
if let Ok(entry) = entry {
let path = entry.path();
if path.is_symlink() {
continue;
}
if path.is_dir() {
if depth <= 5 {
let child_node = Arc::new(RwLock::new(FileNode {
name: path.file_name().unwrap().to_str().unwrap().to_string(),
path: path.to_str().unwrap().to_string(),
value: 0,
children: vec![],
}));
current_node.write().unwrap().children.push(child_node);
let child_size = explore_directory(path.to_str().unwrap(), Arc::clone(¤t_node.read().unwrap().children.last().unwrap()), depth + 1);
current_node.write().unwrap().value += child_size;
total_size += child_size;
} else {
let child_size = explore_directory(path.to_str().unwrap(), Arc::new(RwLock::new(FileNode::empty())), depth + 1);
current_node.write().unwrap().value += child_size;
total_size += child_size;
}
} else if let Ok(metadata) = std::fs::metadata(&path) {
let file_size = metadata.len();
current_node.write().unwrap().value += file_size;
total_size += file_size;
if file_size > 5 * 1024 * 1024 && depth <= 5 {
let child_node = FileNode {
name: path.file_name().unwrap().to_str().unwrap().to_string(),
path: path.to_str().unwrap().to_string(),
value: file_size,
children: vec![],
};
current_node.write().unwrap().children.push(Arc::new(RwLock::new(child_node)));
}
}
}
}
}
total_size
}
此处进行了一部分优化,分别是递归深度>5和文件<5Mb时,仅统计大小,不将文件加入FileNode中,原因是如果什么都加入FileNode,整个对象就会非常大,尤其是全盘扫描的时候。
剩下的一些小细节的部分我就忽略啦!
4.效果
此工具完全开源,希望各位能够提供宝贵的意见,谢谢啦。