用Go写一个矩形树图硬盘分析工具

先来看看效果图

支持下钻, 返回上级目录

速度非常快,只需要0.55秒, 就可以扫描整个C盘


项目使用的了Go语言+React,

Go的框架是Walis

https://wails.io/zh-Hans/docs/introduction/

图表库用的是

https://charts.ant.design/zh


项目的源码在

https://github.com/liu-wh/DiskStatistics

这里贴一下核心的代码, 就是如何实现递归扫描整个C盘的

package main

import (
	"context"
	"fmt"
	"github.com/panjf2000/ants/v2"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"

	"github.com/shirou/gopsutil/v3/disk"
)

var (
	RootPath string
)

type DiskTree struct {
	Name     string `json:"name"`
	Children []any  `json:"children"`
}

type DirNode struct {
	Children []any  `json:"children"`
	Name     string `json:"name"`
}

type Node struct {
	Name  string `json:"name"`
	Value int    `json:"value"`
}

func scanDirTwo(path string, wait *sync.WaitGroup, node *DirNode, pool *ants.Pool) {
	//读取目录下的文件信息
	dirAry, err := os.ReadDir(path)
	if err != nil {
		return
	}
	for _, e := range dirAry {
		//是目录 递归调用,并且创建一个硬盘树节点
		if e.IsDir() {
			wait.Add(1)
			childNode := &DirNode{Name: e.Name(), Children: make([]any, 0, 100)}
			node.Children = append(node.Children, childNode)
			_ = pool.Submit(
				func() {
					scanDirTwo(filepath.Join(path, e.Name()), wait, childNode, pool)
					wait.Done()
				})
		} else {
			//是文件, 存入到硬盘树节点
			info, err := e.Info()
			if err != nil {
				continue
			}
			childNode := &Node{Name: e.Name(), Value: int(info.Size())}
			node.Children = append(node.Children, childNode)
		}

	}
}

// App struct
type App struct {
	ctx context.Context
}


func (a *App) DiskTreeMapStatistics() DiskTree {

	//初始化线程池
	pool, _ := ants.NewPool(1000)
	defer pool.Release()
	start := time.Now()

	//初始化一颗硬盘树
	diskTree := DiskTree{Name: RootPath}
	paths, _ := os.ReadDir(RootPath)
	diskTree.Children = make([]any, 0, len(paths))

	//获取根目录的剩余空间
	d, _ := disk.Usage(RootPath)
	diskTree.Children = append(diskTree.Children, Node{Name: "可用空间", Value: int(d.Free)})
	var (
		loopWg sync.WaitGroup
		info   os.FileInfo
		err    error
	)

	//开始扫描根目录下的目录
	for _, p := range paths {
		var wait sync.WaitGroup
		loopWg.Add(1)
		wait.Add(1)
		if info, err = p.Info(); err != nil {
			continue
		}
		//如果是目录, 使用另外一个函数开始扫描
		if p.IsDir() {
			node := &DirNode{Name: p.Name(), Children: make([]any, 0, 50)}
			diskTree.Children = append(diskTree.Children, node)
			_ = pool.Submit(func() {
				scanDirTwo(filepath.Join(RootPath, p.Name()), &wait, node, pool)
				wait.Done()
			})
		} else {
			//不是目录的话, 把文件信息存到硬盘树
			wait.Done()
			node := &Node{Name: p.Name(), Value: int(info.Size())}
			diskTree.Children = append(diskTree.Children, node)
		}
		wait.Wait()
		loopWg.Done()
	}
	loopWg.Wait()
	log.Printf("扫描结束, 用时%s秒\n", fmt.Sprintf("%0.2f", time.Since(start).Seconds()))
	return diskTree
}

// StartSan 前端点击扫描, 就会调用这个函数
func (a *App) StartSan() DiskTree {
	return a.DiskTreeMapStatistics()

}

评论

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注