A:增加工具函数;增加部分测试照片
No files matched your search
@@ -0,0 +1 @@
|
|||||||
|
/frame/
|
||||||
|
After Width: | Height: | Size: 602 KiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 930 KiB |
|
After Width: | Height: | Size: 355 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 189 KiB |
|
After Width: | Height: | Size: 8.7 MiB |
|
After Width: | Height: | Size: 9.9 MiB |
|
After Width: | Height: | Size: 9.8 MiB |
|
After Width: | Height: | Size: 7.9 MiB |
|
After Width: | Height: | Size: 9.1 MiB |
|
After Width: | Height: | Size: 1.2 MiB |
|
After Width: | Height: | Size: 173 KiB |
@@ -0,0 +1,497 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// FileInfo 文件信息结构体
|
||||||
|
type FileInfo struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Size int64 `json:"size"`
|
||||||
|
ModTime time.Time `json:"mod_time"`
|
||||||
|
IsDir bool `json:"is_dir"`
|
||||||
|
Ext string `json:"ext"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1. 检查文件或目录是否存在
|
||||||
|
func Exists(path string) bool {
|
||||||
|
_, err := os.Stat(path)
|
||||||
|
return !os.IsNotExist(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. 检查是否为目录
|
||||||
|
func IsDir(path string) bool {
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return info.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 检查是否为文件
|
||||||
|
func IsFile(path string) bool {
|
||||||
|
info, err := os.Stat(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return !info.IsDir()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 获取指定目录下的所有文件夹
|
||||||
|
func GetDirectories(dirPath string) ([]string, error) {
|
||||||
|
var dirs []string
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
dirs = append(dirs, entry.Name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return dirs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 获取指定目录下指定格式的文件
|
||||||
|
func GetFilesByExtension(dirPath string, extensions ...string) ([]string, error) {
|
||||||
|
var files []string
|
||||||
|
|
||||||
|
// 标准化扩展名(确保以.开头)
|
||||||
|
for i, ext := range extensions {
|
||||||
|
if !strings.HasPrefix(ext, ".") {
|
||||||
|
extensions[i] = "." + ext
|
||||||
|
}
|
||||||
|
extensions[i] = strings.ToLower(extensions[i])
|
||||||
|
}
|
||||||
|
|
||||||
|
entries, err := os.ReadDir(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() {
|
||||||
|
fileExt := strings.ToLower(filepath.Ext(entry.Name()))
|
||||||
|
for _, ext := range extensions {
|
||||||
|
if fileExt == ext {
|
||||||
|
files = append(files, entry.Name())
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6. 找到指定目录下第一个符合格式的文件
|
||||||
|
func GetFirstFileByExtension(dirPath string, extensions ...string) (string, error) {
|
||||||
|
files, err := GetFilesByExtension(dirPath, extensions...)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(files) == 0 {
|
||||||
|
return "", fmt.Errorf("未找到符合扩展名的文件")
|
||||||
|
}
|
||||||
|
|
||||||
|
return files[0], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 递归获取目录下所有文件
|
||||||
|
func GetAllFiles(dirPath string, recursive bool) ([]FileInfo, error) {
|
||||||
|
var files []FileInfo
|
||||||
|
|
||||||
|
if recursive {
|
||||||
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
files = append(files, FileInfo{
|
||||||
|
Name: info.Name(),
|
||||||
|
Path: path,
|
||||||
|
Size: info.Size(),
|
||||||
|
ModTime: info.ModTime(),
|
||||||
|
IsDir: false,
|
||||||
|
Ext: filepath.Ext(info.Name()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return files, err
|
||||||
|
} else {
|
||||||
|
entries, err := os.ReadDir(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() {
|
||||||
|
info, err := entry.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
files = append(files, FileInfo{
|
||||||
|
Name: info.Name(),
|
||||||
|
Path: filepath.Join(dirPath, info.Name()),
|
||||||
|
Size: info.Size(),
|
||||||
|
ModTime: info.ModTime(),
|
||||||
|
IsDir: false,
|
||||||
|
Ext: filepath.Ext(info.Name()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return files, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 递归获取目录下所有文件夹
|
||||||
|
func GetAllDirs(dirPath string, recursive bool) ([]FileInfo, error) {
|
||||||
|
var dirs []FileInfo
|
||||||
|
|
||||||
|
if recursive {
|
||||||
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只收集目录,排除根目录本身
|
||||||
|
if info.IsDir() && path != dirPath {
|
||||||
|
dirs = append(dirs, FileInfo{
|
||||||
|
Name: info.Name(),
|
||||||
|
Path: path,
|
||||||
|
Size: 0, // 目录大小通常为0或不适用
|
||||||
|
ModTime: info.ModTime(),
|
||||||
|
IsDir: true,
|
||||||
|
Ext: "", // 目录没有扩展名
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return dirs, err
|
||||||
|
} else {
|
||||||
|
entries, err := os.ReadDir(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if entry.IsDir() {
|
||||||
|
info, err := entry.Info()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
dirs = append(dirs, FileInfo{
|
||||||
|
Name: info.Name(),
|
||||||
|
Path: filepath.Join(dirPath, info.Name()),
|
||||||
|
Size: 0, // 目录大小通常为0或不适用
|
||||||
|
ModTime: info.ModTime(),
|
||||||
|
IsDir: true,
|
||||||
|
Ext: "", // 目录没有扩展名
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return dirs, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 删除文件或目录
|
||||||
|
func Delete(path string) error {
|
||||||
|
if !Exists(path) {
|
||||||
|
return fmt.Errorf("路径不存在: %s", path)
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.RemoveAll(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 创建目录(递归创建)
|
||||||
|
func CreateDir(dirPath string) error {
|
||||||
|
return os.MkdirAll(dirPath, 0755)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. 复制文件
|
||||||
|
func CopyFile(src, dst string) error {
|
||||||
|
sourceFile, err := os.Open(src)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("打开源文件失败: %w", err)
|
||||||
|
}
|
||||||
|
defer sourceFile.Close()
|
||||||
|
|
||||||
|
// 确保目标目录存在
|
||||||
|
dstDir := filepath.Dir(dst)
|
||||||
|
if err := CreateDir(dstDir); err != nil {
|
||||||
|
return fmt.Errorf("创建目标目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
destFile, err := os.Create(dst)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("创建目标文件失败: %w", err)
|
||||||
|
}
|
||||||
|
defer destFile.Close()
|
||||||
|
|
||||||
|
_, err = io.Copy(destFile, sourceFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("复制文件失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 11. 移动/重命名文件
|
||||||
|
func MoveFile(src, dst string) error {
|
||||||
|
// 确保目标目录存在
|
||||||
|
dstDir := filepath.Dir(dst)
|
||||||
|
if err := CreateDir(dstDir); err != nil {
|
||||||
|
return fmt.Errorf("创建目标目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.Rename(src, dst)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 12. 读取文件全部内容
|
||||||
|
func ReadFile(filePath string) (string, error) {
|
||||||
|
content, err := os.ReadFile(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("读取文件失败: %w", err)
|
||||||
|
}
|
||||||
|
return string(content), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 13. 按行读取文件
|
||||||
|
func ReadFileLines(filePath string) ([]string, error) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("打开文件失败: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var lines []string
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
lines = append(lines, scanner.Text())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
return nil, fmt.Errorf("读取文件失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return lines, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 14. 写入文件内容
|
||||||
|
func WriteFile(filePath, content string) error {
|
||||||
|
// 确保目录存在
|
||||||
|
dir := filepath.Dir(filePath)
|
||||||
|
if err := CreateDir(dir); err != nil {
|
||||||
|
return fmt.Errorf("创建目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return os.WriteFile(filePath, []byte(content), 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 15. 追加写入文件
|
||||||
|
func AppendFile(filePath, content string) error {
|
||||||
|
// 确保目录存在
|
||||||
|
dir := filepath.Dir(filePath)
|
||||||
|
if err := CreateDir(dir); err != nil {
|
||||||
|
return fmt.Errorf("创建目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.OpenFile(filePath, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("打开文件失败: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
_, err = file.WriteString(content)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 16. 获取文件大小
|
||||||
|
func GetFileSize(filePath string) (int64, error) {
|
||||||
|
info, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return 0, fmt.Errorf("获取文件信息失败: %w", err)
|
||||||
|
}
|
||||||
|
return info.Size(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 17. 获取文件修改时间
|
||||||
|
func GetModTime(filePath string) (time.Time, error) {
|
||||||
|
info, err := os.Stat(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return time.Time{}, fmt.Errorf("获取文件信息失败: %w", err)
|
||||||
|
}
|
||||||
|
return info.ModTime(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 18. 获取文件扩展名
|
||||||
|
func GetExtension(filePath string) string {
|
||||||
|
return filepath.Ext(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 19. 获取文件名(不含扩展名)
|
||||||
|
func GetFileName(filePath string) string {
|
||||||
|
base := filepath.Base(filePath)
|
||||||
|
ext := filepath.Ext(base)
|
||||||
|
return strings.TrimSuffix(base, ext)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 20. 获取文件基本名称(含扩展名)
|
||||||
|
func GetBaseName(filePath string) string {
|
||||||
|
return filepath.Base(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 21. 获取文件目录
|
||||||
|
func GetDir(filePath string) string {
|
||||||
|
return filepath.Dir(filePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 22. 计算文件MD5哈希值
|
||||||
|
func GetFileMD5(filePath string) (string, error) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("打开文件失败: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
hash := md5.New()
|
||||||
|
if _, err := io.Copy(hash, file); err != nil {
|
||||||
|
return "", fmt.Errorf("计算哈希失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", hash.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 23. 计算文件SHA256哈希值
|
||||||
|
func GetFileSHA256(filePath string) (string, error) {
|
||||||
|
file, err := os.Open(filePath)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("打开文件失败: %w", err)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
hash := sha256.New()
|
||||||
|
if _, err := io.Copy(hash, file); err != nil {
|
||||||
|
return "", fmt.Errorf("计算哈希失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", hash.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 24. 清空目录内容(保留目录本身)
|
||||||
|
func CleanDir(dirPath string) error {
|
||||||
|
entries, err := os.ReadDir(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("读取目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
path := filepath.Join(dirPath, entry.Name())
|
||||||
|
if err := os.RemoveAll(path); err != nil {
|
||||||
|
return fmt.Errorf("删除 %s 失败: %w", path, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 25. 获取目录大小
|
||||||
|
func GetDirSize(dirPath string) (int64, error) {
|
||||||
|
var size int64
|
||||||
|
|
||||||
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if !info.IsDir() {
|
||||||
|
size += info.Size()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
|
||||||
|
return size, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 26. 格式化文件大小
|
||||||
|
func FormatFileSize(bytes int64) string {
|
||||||
|
const unit = 1024
|
||||||
|
if bytes < unit {
|
||||||
|
return fmt.Sprintf("%d B", bytes)
|
||||||
|
}
|
||||||
|
div, exp := int64(unit), 0
|
||||||
|
for n := bytes / unit; n >= unit; n /= unit {
|
||||||
|
div *= unit
|
||||||
|
exp++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
|
||||||
|
}
|
||||||
|
|
||||||
|
// 27. 搜索文件(按文件名模式)
|
||||||
|
func SearchFiles(dirPath, pattern string, recursive bool) ([]string, error) {
|
||||||
|
var matches []string
|
||||||
|
|
||||||
|
if recursive {
|
||||||
|
err := filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !info.IsDir() {
|
||||||
|
matched, err := filepath.Match(pattern, info.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
matches = append(matches, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return matches, err
|
||||||
|
} else {
|
||||||
|
entries, err := os.ReadDir(dirPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取目录失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, entry := range entries {
|
||||||
|
if !entry.IsDir() {
|
||||||
|
matched, err := filepath.Match(pattern, entry.Name())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if matched {
|
||||||
|
matches = append(matches, filepath.Join(dirPath, entry.Name()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return matches, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 28. 获取当前工作目录
|
||||||
|
func GetCurrentDir() (string, error) {
|
||||||
|
return os.Getwd()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 29. 改变工作目录
|
||||||
|
func ChangeDir(dirPath string) error {
|
||||||
|
return os.Chdir(dirPath)
|
||||||
|
}
|
||||||
@@ -0,0 +1,349 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
// HashType 定义支持的Hash算法类型
|
||||||
|
type HashType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
MD5 HashType = iota
|
||||||
|
SHA1
|
||||||
|
SHA256
|
||||||
|
SHA512
|
||||||
|
)
|
||||||
|
|
||||||
|
// 缓冲区大小配置
|
||||||
|
const (
|
||||||
|
SmallFileThreshold = 1024 * 1024 // 1MB,小文件阈值
|
||||||
|
DefaultBufferSize = 64 * 1024 // 64KB,默认缓冲区
|
||||||
|
LargeBufferSize = 1024 * 1024 // 1MB,大文件缓冲区
|
||||||
|
)
|
||||||
|
|
||||||
|
// HashResult 存储Hash计算结果
|
||||||
|
type HashResult struct {
|
||||||
|
Filename string
|
||||||
|
Hash string
|
||||||
|
Error error
|
||||||
|
Size int64
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHasher 根据类型返回对应的Hash实例
|
||||||
|
func getHasher(hashType HashType) hash.Hash {
|
||||||
|
switch hashType {
|
||||||
|
case MD5:
|
||||||
|
return md5.New()
|
||||||
|
case SHA1:
|
||||||
|
return sha1.New()
|
||||||
|
case SHA256:
|
||||||
|
return sha256.New()
|
||||||
|
case SHA512:
|
||||||
|
return sha512.New()
|
||||||
|
default:
|
||||||
|
return sha256.New()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getHasherName 获取Hash算法名称
|
||||||
|
func getHasherName(hashType HashType) string {
|
||||||
|
switch hashType {
|
||||||
|
case MD5:
|
||||||
|
return "MD5"
|
||||||
|
case SHA1:
|
||||||
|
return "SHA1"
|
||||||
|
case SHA256:
|
||||||
|
return "SHA256"
|
||||||
|
case SHA512:
|
||||||
|
return "SHA512"
|
||||||
|
default:
|
||||||
|
return "SHA256"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashString 计算字符串的Hash值
|
||||||
|
func HashString(data string, hashType HashType) string {
|
||||||
|
hasher := getHasher(hashType)
|
||||||
|
hasher.Write([]byte(data))
|
||||||
|
return fmt.Sprintf("%x", hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashBytes 计算字节数组的Hash值
|
||||||
|
func HashBytes(data []byte, hashType HashType) string {
|
||||||
|
hasher := getHasher(hashType)
|
||||||
|
hasher.Write(data)
|
||||||
|
return fmt.Sprintf("%x", hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFile 计算单个文件的Hash值(自动选择优化策略)
|
||||||
|
func HashFile(filename string, hashType HashType) (string, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 获取文件信息
|
||||||
|
fileInfo, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher := getHasher(hashType)
|
||||||
|
|
||||||
|
// 根据文件大小选择不同的处理策略
|
||||||
|
if fileInfo.Size() <= SmallFileThreshold {
|
||||||
|
// 小文件:直接读取
|
||||||
|
return hashSmallFile(file, hasher)
|
||||||
|
} else {
|
||||||
|
// 大文件:使用缓冲区
|
||||||
|
return hashLargeFile(file, hasher, fileInfo.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashSmallFile 处理小文件
|
||||||
|
func hashSmallFile(file *os.File, hasher hash.Hash) (string, error) {
|
||||||
|
_, err := io.Copy(hasher, file)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%x", hasher.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// hashLargeFile 处理大文件
|
||||||
|
func hashLargeFile(file *os.File, hasher hash.Hash, fileSize int64) (string, error) {
|
||||||
|
// 根据文件大小动态调整缓冲区
|
||||||
|
bufferSize := DefaultBufferSize
|
||||||
|
if fileSize > 100*1024*1024 { // 大于100MB使用更大缓冲区
|
||||||
|
bufferSize = LargeBufferSize
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := make([]byte, bufferSize)
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := file.Read(buffer)
|
||||||
|
if n > 0 {
|
||||||
|
hasher.Write(buffer[:n])
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", hasher.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFileWithProgress 计算文件Hash值并提供进度回调
|
||||||
|
func HashFileWithProgress(filename string, hashType HashType, progressCallback func(processed, total int64)) (string, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
fileInfo, err := file.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
hasher := getHasher(hashType)
|
||||||
|
buffer := make([]byte, DefaultBufferSize)
|
||||||
|
var processed int64
|
||||||
|
|
||||||
|
for {
|
||||||
|
n, err := file.Read(buffer)
|
||||||
|
if n > 0 {
|
||||||
|
hasher.Write(buffer[:n])
|
||||||
|
processed += int64(n)
|
||||||
|
if progressCallback != nil {
|
||||||
|
progressCallback(processed, fileInfo.Size())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Sprintf("%x", hasher.Sum(nil)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashMultipleFiles 并发计算多个文件的Hash值
|
||||||
|
func HashMultipleFiles(filenames []string, hashType HashType) []HashResult {
|
||||||
|
// 使用CPU核心数作为并发数
|
||||||
|
numWorkers := runtime.NumCPU()
|
||||||
|
jobs := make(chan string, len(filenames))
|
||||||
|
results := make(chan HashResult, len(filenames))
|
||||||
|
|
||||||
|
// 启动工作协程
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
for i := 0; i < numWorkers; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
for filename := range jobs {
|
||||||
|
hash, err := HashFile(filename, hashType)
|
||||||
|
|
||||||
|
var size int64
|
||||||
|
if err == nil {
|
||||||
|
if fileInfo, statErr := os.Stat(filename); statErr == nil {
|
||||||
|
size = fileInfo.Size()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results <- HashResult{
|
||||||
|
Filename: filename,
|
||||||
|
Hash: hash,
|
||||||
|
Error: err,
|
||||||
|
Size: size,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 发送任务
|
||||||
|
go func() {
|
||||||
|
for _, filename := range filenames {
|
||||||
|
jobs <- filename
|
||||||
|
}
|
||||||
|
close(jobs)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 等待所有工作完成
|
||||||
|
go func() {
|
||||||
|
wg.Wait()
|
||||||
|
close(results)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// 收集结果
|
||||||
|
var hashResults []HashResult
|
||||||
|
for result := range results {
|
||||||
|
hashResults = append(hashResults, result)
|
||||||
|
}
|
||||||
|
|
||||||
|
return hashResults
|
||||||
|
}
|
||||||
|
|
||||||
|
// CompareFiles 比较两个文件是否相同(通过Hash值)
|
||||||
|
func CompareFiles(file1, file2 string, hashType HashType) (bool, error) {
|
||||||
|
hash1, err := HashFile(file1, hashType)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
hash2, err := HashFile(file2, hashType)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash1 == hash2, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FindDuplicates 在文件列表中查找重复文件
|
||||||
|
func FindDuplicates(filenames []string, hashType HashType) map[string][]string {
|
||||||
|
results := HashMultipleFiles(filenames, hashType)
|
||||||
|
duplicates := make(map[string][]string)
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
if result.Error == nil {
|
||||||
|
duplicates[result.Hash] = append(duplicates[result.Hash], result.Filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 只保留有重复的Hash
|
||||||
|
for hash, files := range duplicates {
|
||||||
|
if len(files) <= 1 {
|
||||||
|
delete(duplicates, hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return duplicates
|
||||||
|
}
|
||||||
|
|
||||||
|
// VerifyFileIntegrity 验证文件完整性
|
||||||
|
func VerifyFileIntegrity(filename, expectedHash string, hashType HashType) (bool, error) {
|
||||||
|
actualHash, err := HashFile(filename, hashType)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 不区分大小写比较
|
||||||
|
return strings.EqualFold(actualHash, expectedHash), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashFileMultipleAlgorithms 使用多种算法计算文件Hash
|
||||||
|
func HashFileMultipleAlgorithms(filename string, hashTypes []HashType) (map[string]string, error) {
|
||||||
|
file, err := os.Open(filename)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
// 创建多个hasher
|
||||||
|
hashers := make(map[HashType]hash.Hash)
|
||||||
|
for _, hashType := range hashTypes {
|
||||||
|
hashers[hashType] = getHasher(hashType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建MultiWriter
|
||||||
|
var writers []io.Writer
|
||||||
|
for _, hasher := range hashers {
|
||||||
|
writers = append(writers, hasher)
|
||||||
|
}
|
||||||
|
multiWriter := io.MultiWriter(writers...)
|
||||||
|
|
||||||
|
// 一次读取,多重计算
|
||||||
|
_, err = io.Copy(multiWriter, file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// 收集结果
|
||||||
|
results := make(map[string]string)
|
||||||
|
for hashType, hasher := range hashers {
|
||||||
|
hashName := getHasherName(hashType)
|
||||||
|
results[hashName] = fmt.Sprintf("%x", hasher.Sum(nil))
|
||||||
|
}
|
||||||
|
|
||||||
|
return results, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// 便捷函数
|
||||||
|
func MD5File(filename string) (string, error) {
|
||||||
|
return HashFile(filename, MD5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SHA1File(filename string) (string, error) {
|
||||||
|
return HashFile(filename, SHA1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SHA256File(filename string) (string, error) {
|
||||||
|
return HashFile(filename, SHA256)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SHA512File(filename string) (string, error) {
|
||||||
|
return HashFile(filename, SHA512)
|
||||||
|
}
|
||||||
|
|
||||||
|
func MD5String(data string) string {
|
||||||
|
return HashString(data, MD5)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SHA256String(data string) string {
|
||||||
|
return HashString(data, SHA256)
|
||||||
|
}
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// 1. 计算字符串Hash
|
||||||
|
fmt.Println("=== 字符串Hash ===")
|
||||||
|
text := "Hello, World!"
|
||||||
|
fmt.Printf("MD5: %s\n", MD5String(text))
|
||||||
|
fmt.Printf("SHA256: %s\n", SHA256String(text))
|
||||||
|
|
||||||
|
// 2. 计算单个文件Hash
|
||||||
|
fmt.Println("\n=== 单个文件Hash ===")
|
||||||
|
filename := "example.txt"
|
||||||
|
if hash, err := SHA256File(filename); err != nil {
|
||||||
|
log.Printf("计算文件Hash失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("文件 %s 的 SHA256: %s\n", filename, hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3. 带进度的文件Hash计算
|
||||||
|
fmt.Println("\n=== 带进度的Hash计算 ===")
|
||||||
|
progressCallback := func(processed, total int64) {
|
||||||
|
percentage := float64(processed) / float64(total) * 100
|
||||||
|
fmt.Printf("\r进度: %.2f%% (%d/%d bytes)", percentage, processed, total)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hash, err := HashFileWithProgress(filename, SHA256, progressCallback); err != nil {
|
||||||
|
log.Printf("计算失败: %v", err)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("\n最终Hash: %s\n", hash)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4. 并发计算多个文件
|
||||||
|
fmt.Println("\n=== 并发计算多个文件 ===")
|
||||||
|
files := []string{"file1.txt", "file2.txt", "file3.txt"}
|
||||||
|
results := HashMultipleFiles(files, SHA256)
|
||||||
|
|
||||||
|
for _, result := range results {
|
||||||
|
if result.Error != nil {
|
||||||
|
fmt.Printf("文件 %s 计算失败: %v\n", result.Filename, result.Error)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("文件 %s (%.2f KB): %s\n",
|
||||||
|
result.Filename,
|
||||||
|
float64(result.Size)/1024,
|
||||||
|
result.Hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5. 计算目录下所有文件
|
||||||
|
// fmt.Println("\n=== 目录Hash计算 ===")
|
||||||
|
// dirResults, err := HashDirectory("./testdir", SHA256, true)
|
||||||
|
// if err != nil {
|
||||||
|
// log.Printf("计算目录Hash失败: %v", err)
|
||||||
|
// } else {
|
||||||
|
// for _, result := range dirResults {
|
||||||
|
// if result.Error == nil {
|
||||||
|
// fmt.Printf("%s: %s\n", result.Filename, result.Hash)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 6. 查找重复文件
|
||||||
|
fmt.Println("\n=== 查找重复文件 ===")
|
||||||
|
allFiles := []string{"file1.txt", "file2.txt", "file3.txt", "file1_copy.txt"}
|
||||||
|
duplicates := FindDuplicates(allFiles, SHA256)
|
||||||
|
|
||||||
|
for hash, files := range duplicates {
|
||||||
|
fmt.Printf("Hash %s 的重复文件:\n", hash[:16]+"...")
|
||||||
|
for _, file := range files {
|
||||||
|
fmt.Printf(" - %s\n", file)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 7. 比较两个文件
|
||||||
|
fmt.Println("\n=== 文件比较 ===")
|
||||||
|
if same, err := CompareFiles("file1.txt", "file2.txt", SHA256); err != nil {
|
||||||
|
log.Printf("文件比较失败: %v", err)
|
||||||
|
} else {
|
||||||
|
if same {
|
||||||
|
fmt.Println("两个文件内容相同")
|
||||||
|
} else {
|
||||||
|
fmt.Println("两个文件内容不同")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 8. 验证文件完整性
|
||||||
|
fmt.Println("\n=== 文件完整性验证 ===")
|
||||||
|
expectedHash := "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" // 空文件的SHA256
|
||||||
|
if valid, err := VerifyFileIntegrity("empty.txt", expectedHash, SHA256); err != nil {
|
||||||
|
log.Printf("验证失败: %v", err)
|
||||||
|
} else {
|
||||||
|
if valid {
|
||||||
|
fmt.Println("文件完整性验证通过")
|
||||||
|
} else {
|
||||||
|
fmt.Println("文件完整性验证失败")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 多算法同时计算
|
||||||
|
fmt.Println("\n=== 多算法Hash计算 ===")
|
||||||
|
hashTypes := []HashType{
|
||||||
|
MD5,
|
||||||
|
SHA1,
|
||||||
|
SHA256,
|
||||||
|
SHA512,
|
||||||
|
}
|
||||||
|
|
||||||
|
multiHashes, err := HashFileMultipleAlgorithms(filename, hashTypes)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("多算法计算失败: %v", err)
|
||||||
|
} else {
|
||||||
|
for algorithm, hash := range multiHashes {
|
||||||
|
fmt.Printf("%s: %s\n", algorithm, hash)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10. 获取文件详细信息
|
||||||
|
// fmt.Println("\n=== 文件详细信息 ===")
|
||||||
|
// if fileInfo, err := GetFileInfo(filename, SHA256); err != nil {
|
||||||
|
// log.Printf("获取文件信息失败: %v", err)
|
||||||
|
// } else {
|
||||||
|
// fmt.Printf("文件名: %s\n", fileInfo.Name)
|
||||||
|
// fmt.Printf("路径: %s\n", fileInfo.Path)
|
||||||
|
// fmt.Printf("大小: %d bytes\n", fileInfo.Size)
|
||||||
|
// fmt.Printf("Hash类型: %s\n", fileInfo.HashType)
|
||||||
|
// fmt.Printf("Hash值: %s\n", fileInfo.Hash)
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 11. 性能测试示例
|
||||||
|
fmt.Println("\n=== 性能测试 ===")
|
||||||
|
start := time.Now()
|
||||||
|
_, err = SHA256File("largefile.txt")
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("大文件Hash计算失败: %v", err)
|
||||||
|
} else {
|
||||||
|
duration := time.Since(start)
|
||||||
|
fmt.Printf("大文件Hash计算耗时: %v\n", duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建测试文件的辅助函数
|
||||||
|
func createTestFiles() {
|
||||||
|
// 这个函数可以用来创建一些测试文件
|
||||||
|
testData := []struct {
|
||||||
|
filename string
|
||||||
|
content string
|
||||||
|
}{
|
||||||
|
{"file1.txt", "Hello World"},
|
||||||
|
{"file2.txt", "Go Programming"},
|
||||||
|
{"file3.txt", "Hash Utils Test"},
|
||||||
|
{"file1_copy.txt", "Hello World"}, // 与file1.txt内容相同
|
||||||
|
{"empty.txt", ""},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range testData {
|
||||||
|
// 创建文件的代码...
|
||||||
|
fmt.Printf("需要创建测试文件: %s\n", test.filename)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,194 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// SystemInfo 系统信息结构体
|
||||||
|
type SystemInfo struct {
|
||||||
|
OS string `json:"os"`
|
||||||
|
Arch string `json:"arch"`
|
||||||
|
CPUCount int `json:"cpu_count"`
|
||||||
|
Is64Bit bool `json:"is_64_bit"`
|
||||||
|
Hostname string `json:"hostname"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
GoVersion string `json:"go_version"`
|
||||||
|
Compiler string `json:"compiler"`
|
||||||
|
NumGoroutine int `json:"num_goroutine"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPUInfo CPU信息结构体
|
||||||
|
type CPUInfo struct {
|
||||||
|
ModelName string `json:"model_name"`
|
||||||
|
Cores int `json:"cores"`
|
||||||
|
Threads int `json:"threads"`
|
||||||
|
Frequency string `json:"frequency"`
|
||||||
|
Usage float64 `json:"usage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// MemoryInfo 内存信息结构体
|
||||||
|
type MemoryInfo struct {
|
||||||
|
Total uint64 `json:"total"`
|
||||||
|
Available uint64 `json:"available"`
|
||||||
|
Used uint64 `json:"used"`
|
||||||
|
UsedPct float64 `json:"used_percent"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// DiskInfo 磁盘信息结构体
|
||||||
|
type DiskInfo struct {
|
||||||
|
Path string `json:"path"` // 磁盘路径
|
||||||
|
Total uint64 `json:"total"` // 总空间 (字节)
|
||||||
|
Free uint64 `json:"free"` // 可用空间 (字节)
|
||||||
|
Used uint64 `json:"used"` // 已使用空间 (字节)
|
||||||
|
UsedPct float64 `json:"used_percent"` // 使用百分比
|
||||||
|
FileSystem string `json:"file_system"` // 文件系统类型
|
||||||
|
}
|
||||||
|
|
||||||
|
// NetworkInfo 网络信息结构体
|
||||||
|
type NetworkInfo struct {
|
||||||
|
InterfaceName string `json:"interface_name"`
|
||||||
|
IPAddress string `json:"ip_address"`
|
||||||
|
MACAddress string `json:"mac_address"`
|
||||||
|
MTU int `json:"mtu"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// ProcessInfo 进程信息结构体
|
||||||
|
type ProcessInfo struct {
|
||||||
|
PID int `json:"pid"`
|
||||||
|
PPID int `json:"ppid"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
CPUPercent float64 `json:"cpu_percent"`
|
||||||
|
MemoryUsage uint64 `json:"memory_usage"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// SysUtils 系统工具类
|
||||||
|
type SysUtils struct{}
|
||||||
|
|
||||||
|
// NewSysUtils 创建新的系统工具实例
|
||||||
|
func NewSysUtils() *SysUtils {
|
||||||
|
return &SysUtils{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSystemInfo 获取基本系统信息
|
||||||
|
func (s *SysUtils) GetSystemInfo() SystemInfo {
|
||||||
|
hostname, _ := os.Hostname()
|
||||||
|
username := os.Getenv("USER")
|
||||||
|
if username == "" {
|
||||||
|
username = os.Getenv("USERNAME") // Windows
|
||||||
|
}
|
||||||
|
|
||||||
|
return SystemInfo{
|
||||||
|
OS: runtime.GOOS,
|
||||||
|
Arch: runtime.GOARCH,
|
||||||
|
CPUCount: runtime.NumCPU(),
|
||||||
|
Is64Bit: s.Is64Bit(),
|
||||||
|
Hostname: hostname,
|
||||||
|
Username: username,
|
||||||
|
GoVersion: runtime.Version(),
|
||||||
|
Compiler: runtime.Compiler,
|
||||||
|
NumGoroutine: runtime.NumGoroutine(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCurrentProcessInfo 获取当前进程信息
|
||||||
|
func (s *SysUtils) GetCurrentProcessInfo() ProcessInfo {
|
||||||
|
pid := os.Getpid()
|
||||||
|
ppid := os.Getppid()
|
||||||
|
|
||||||
|
var m runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&m)
|
||||||
|
|
||||||
|
return ProcessInfo{
|
||||||
|
PID: pid,
|
||||||
|
PPID: ppid,
|
||||||
|
Name: os.Args[0],
|
||||||
|
MemoryUsage: m.Alloc,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetSystemUptime 获取系统启动时间
|
||||||
|
func (s *SysUtils) GetSystemUptime() time.Duration {
|
||||||
|
return s.getSystemUptime()
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatBytes 格式化字节数为人类可读格式
|
||||||
|
func (s *SysUtils) FormatBytes(bytes uint64) string {
|
||||||
|
const unit = 1024
|
||||||
|
if bytes < unit {
|
||||||
|
return fmt.Sprintf("%d B", bytes)
|
||||||
|
}
|
||||||
|
div, exp := uint64(unit), 0
|
||||||
|
for n := bytes / unit; n >= unit; n /= unit {
|
||||||
|
div *= unit
|
||||||
|
exp++
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp])
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintSystemInfo 打印完整的系统信息
|
||||||
|
func (s *SysUtils) PrintSystemInfo() {
|
||||||
|
fmt.Println("========== 系统信息 ==========")
|
||||||
|
sysInfo := s.GetSystemInfo()
|
||||||
|
fmt.Printf("操作系统: %s\n", sysInfo.OS)
|
||||||
|
fmt.Printf("架构: %s\n", sysInfo.Arch)
|
||||||
|
fmt.Printf("CPU核心数: %d\n", sysInfo.CPUCount)
|
||||||
|
fmt.Printf("64位系统: %v\n", sysInfo.Is64Bit)
|
||||||
|
fmt.Printf("主机名: %s\n", sysInfo.Hostname)
|
||||||
|
fmt.Printf("用户名: %s\n", sysInfo.Username)
|
||||||
|
fmt.Printf("Go版本: %s\n", sysInfo.GoVersion)
|
||||||
|
fmt.Printf("编译器: %s\n", sysInfo.Compiler)
|
||||||
|
fmt.Printf("Goroutine数量: %d\n", sysInfo.NumGoroutine)
|
||||||
|
|
||||||
|
fmt.Println("\n========== CPU信息 ==========")
|
||||||
|
cpuInfo := s.GetCPUInfo()
|
||||||
|
fmt.Printf("型号: %s\n", cpuInfo.ModelName)
|
||||||
|
fmt.Printf("核心数: %d\n", cpuInfo.Cores)
|
||||||
|
fmt.Printf("线程数: %d\n", cpuInfo.Threads)
|
||||||
|
|
||||||
|
fmt.Println("\n========== 内存信息 ==========")
|
||||||
|
memInfo := s.GetMemoryInfo()
|
||||||
|
fmt.Printf("总内存: %s\n", s.FormatBytes(memInfo.Total))
|
||||||
|
fmt.Printf("已用内存: %s\n", s.FormatBytes(memInfo.Used))
|
||||||
|
fmt.Printf("可用内存: %s\n", s.FormatBytes(memInfo.Available))
|
||||||
|
fmt.Printf("使用率: %.2f%%\n", memInfo.UsedPct)
|
||||||
|
|
||||||
|
fmt.Println("\n========== 磁盘信息 ==========")
|
||||||
|
diskInfo, _ := s.GetDiskUsage(".")
|
||||||
|
if diskInfo != nil {
|
||||||
|
fmt.Printf("路径: %s\n", diskInfo.Path)
|
||||||
|
fmt.Printf("总空间: %s\n", s.FormatBytes(diskInfo.Total))
|
||||||
|
fmt.Printf("已用空间: %s\n", s.FormatBytes(diskInfo.Used))
|
||||||
|
fmt.Printf("可用空间: %s\n", s.FormatBytes(diskInfo.Free))
|
||||||
|
fmt.Printf("使用率: %.2f%%\n", diskInfo.UsedPct)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n========== 网络接口 ==========")
|
||||||
|
networkInfos := s.GetNetworkInterfaces()
|
||||||
|
for _, netInfo := range networkInfos {
|
||||||
|
fmt.Printf("接口: %s\n", netInfo.InterfaceName)
|
||||||
|
fmt.Printf("IP地址: %s\n", netInfo.IPAddress)
|
||||||
|
fmt.Printf("MAC地址: %s\n", netInfo.MACAddress)
|
||||||
|
fmt.Printf("MTU: %d\n", netInfo.MTU)
|
||||||
|
fmt.Println("---")
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n========== GPU信息 ==========")
|
||||||
|
gpus := s.GetGPUInfo()
|
||||||
|
for i, gpu := range gpus {
|
||||||
|
fmt.Printf("GPU %d: %s\n", i+1, gpu)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("\n========== 进程信息 ==========")
|
||||||
|
processInfo := s.GetCurrentProcessInfo()
|
||||||
|
fmt.Printf("进程ID: %d\n", processInfo.PID)
|
||||||
|
fmt.Printf("父进程ID: %d\n", processInfo.PPID)
|
||||||
|
fmt.Printf("进程名: %s\n", processInfo.Name)
|
||||||
|
fmt.Printf("内存使用: %s\n", s.FormatBytes(processInfo.MemoryUsage))
|
||||||
|
|
||||||
|
fmt.Println("\n========== 系统运行时间 ==========")
|
||||||
|
uptime := s.GetSystemUptime()
|
||||||
|
fmt.Printf("运行时间: %s\n", uptime.String())
|
||||||
|
}
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"runtime"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Is64Bit 检查系统是否为64位
|
||||||
|
func (s *SysUtils) Is64Bit() bool {
|
||||||
|
return unsafe.Sizeof(uintptr(0)) == 8
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetNetworkInterfaces 获取网络接口信息
|
||||||
|
func (s *SysUtils) GetNetworkInterfaces() []NetworkInfo {
|
||||||
|
var networkInfos []NetworkInfo
|
||||||
|
|
||||||
|
interfaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
return networkInfos
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, iface := range interfaces {
|
||||||
|
// 跳过回环接口和未启用的接口
|
||||||
|
if iface.Flags&net.FlagLoopback != 0 || iface.Flags&net.FlagUp == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
addrs, err := iface.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var ipAddr string
|
||||||
|
for _, addr := range addrs {
|
||||||
|
if ipNet, ok := addr.(*net.IPNet); ok && !ipNet.IP.IsLoopback() {
|
||||||
|
if ipNet.IP.To4() != nil {
|
||||||
|
ipAddr = ipNet.IP.String()
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ipAddr != "" {
|
||||||
|
networkInfo := NetworkInfo{
|
||||||
|
InterfaceName: iface.Name,
|
||||||
|
IPAddress: ipAddr,
|
||||||
|
MACAddress: iface.HardwareAddr.String(),
|
||||||
|
MTU: iface.MTU,
|
||||||
|
}
|
||||||
|
networkInfos = append(networkInfos, networkInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return networkInfos
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetCPUInfo 获取CPU信息
|
||||||
|
func (s *SysUtils) GetCPUInfo() CPUInfo {
|
||||||
|
cpuInfo := CPUInfo{
|
||||||
|
Cores: runtime.NumCPU(),
|
||||||
|
Threads: runtime.NumCPU(), // 在Go中通常相等
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用平台特定的方法来获取详细信息
|
||||||
|
s.getCPUInfoPlatform(&cpuInfo)
|
||||||
|
|
||||||
|
return cpuInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMemoryInfo 获取内存信息
|
||||||
|
func (s *SysUtils) GetMemoryInfo() MemoryInfo {
|
||||||
|
var memInfo MemoryInfo
|
||||||
|
|
||||||
|
// 调用平台特定的方法
|
||||||
|
s.getMemoryInfoPlatform(&memInfo)
|
||||||
|
|
||||||
|
if memInfo.Total > 0 {
|
||||||
|
memInfo.UsedPct = float64(memInfo.Used) / float64(memInfo.Total) * 100
|
||||||
|
}
|
||||||
|
|
||||||
|
return memInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetDiskUsage 获取磁盘使用情况
|
||||||
|
func (s *SysUtils) GetDiskUsage(path string) (*DiskInfo, error) {
|
||||||
|
return s.getDiskUsagePlatform(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetAllDisksInfo 跨平台获取所有磁盘信息
|
||||||
|
func (s *SysUtils) GetAllDisksInfo() ([]*DiskInfo, error) {
|
||||||
|
return s.getAllDisksInfoPlatform()
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGPUInfo 获取GPU信息
|
||||||
|
func (s *SysUtils) GetGPUInfo() []string {
|
||||||
|
return s.getGPUInfoPlatform()
|
||||||
|
}
|
||||||
@@ -0,0 +1,171 @@
|
|||||||
|
//go:build darwin
|
||||||
|
// +build darwin
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getCPUInfoPlatform 获取macOS下的CPU信息
|
||||||
|
func (s *SysUtils) getCPUInfoPlatform(info *CPUInfo) {
|
||||||
|
cmd := exec.Command("sysctl", "-n", "machdep.cpu.brand_string")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.ModelName = strings.TrimSpace(string(output))
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMemoryInfoPlatform 获取macOS下的内存信息
|
||||||
|
func (s *SysUtils) getMemoryInfoPlatform(info *MemoryInfo) {
|
||||||
|
cmd := exec.Command("sysctl", "-n", "hw.memsize")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
total, err := strconv.ParseUint(strings.TrimSpace(string(output)), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
info.Total = total
|
||||||
|
|
||||||
|
// 获取可用内存需要更复杂的计算,这里简化处理
|
||||||
|
var m runtime.MemStats
|
||||||
|
runtime.ReadMemStats(&m)
|
||||||
|
info.Used = m.Sys
|
||||||
|
info.Available = info.Total - info.Used
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDiskUsagePlatform macOS平台获取磁盘信息
|
||||||
|
func (s *SysUtils) getDiskUsagePlatform(path string) (*DiskInfo, error) {
|
||||||
|
var stat syscall.Statfs_t
|
||||||
|
err := syscall.Statfs(path, &stat)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取磁盘信息失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
total := stat.Blocks * uint64(stat.Bsize)
|
||||||
|
free := stat.Bavail * uint64(stat.Bsize)
|
||||||
|
used := total - free
|
||||||
|
usedPct := float64(used) / float64(total) * 100
|
||||||
|
|
||||||
|
// 获取文件系统类型
|
||||||
|
fsType := "APFS" // macOS默认文件系统
|
||||||
|
|
||||||
|
return &DiskInfo{
|
||||||
|
Path: path,
|
||||||
|
Total: total,
|
||||||
|
Free: free,
|
||||||
|
Used: used,
|
||||||
|
UsedPct: usedPct,
|
||||||
|
FileSystem: fsType,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAllDisksInfoPlatform macOS平台获取所有磁盘信息
|
||||||
|
func (s *SysUtils) getAllDisksInfoPlatform() ([]*DiskInfo, error) {
|
||||||
|
var disks []*DiskInfo
|
||||||
|
|
||||||
|
// 获取挂载的卷
|
||||||
|
cmd := exec.Command("df", "-h")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
// 如果df命令失败,使用默认路径
|
||||||
|
commonPaths := []string{"/", "/System/Volumes/Data"}
|
||||||
|
for _, path := range commonPaths {
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
diskInfo, err := s.GetDiskUsage(path)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
disks = append(disks, diskInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return disks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for i, line := range lines {
|
||||||
|
if i == 0 || line == "" { // 跳过标题行和空行
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 6 {
|
||||||
|
mountPoint := fields[5]
|
||||||
|
// 过滤掉一些虚拟文件系统
|
||||||
|
if strings.HasPrefix(mountPoint, "/dev") ||
|
||||||
|
strings.HasPrefix(mountPoint, "/private/var/vm") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
diskInfo, err := s.GetDiskUsage(mountPoint)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
disks = append(disks, diskInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return disks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGPUInfoPlatform 获取macOS下的GPU信息
|
||||||
|
func (s *SysUtils) getGPUInfoPlatform() []string {
|
||||||
|
var gpus []string
|
||||||
|
|
||||||
|
// 使用system_profiler获取GPU信息
|
||||||
|
cmd := exec.Command("system_profiler", "SPDisplaysDataType")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return gpus
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(line, "Chipset Model:") {
|
||||||
|
parts := strings.Split(line, ":")
|
||||||
|
if len(parts) > 1 {
|
||||||
|
gpu := strings.TrimSpace(parts[1])
|
||||||
|
gpus = append(gpus, gpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpus
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSystemUptime 获取macOS系统启动时间
|
||||||
|
func (s *SysUtils) getSystemUptime() time.Duration {
|
||||||
|
cmd := exec.Command("sysctl", "-n", "kern.boottime")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析输出格式: { sec = 1234567890, usec = 123456 }
|
||||||
|
line := strings.TrimSpace(string(output))
|
||||||
|
if strings.Contains(line, "sec =") {
|
||||||
|
parts := strings.Split(line, "sec =")
|
||||||
|
if len(parts) > 1 {
|
||||||
|
secPart := strings.Split(parts[1], ",")[0]
|
||||||
|
secPart = strings.TrimSpace(secPart)
|
||||||
|
sec, err := strconv.ParseInt(secPart, 10, 64)
|
||||||
|
if err == nil {
|
||||||
|
bootTime := time.Unix(sec, 0)
|
||||||
|
return time.Since(bootTime)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,200 @@
|
|||||||
|
//go:build linux
|
||||||
|
// +build linux
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getCPUInfoPlatform 获取Linux下的CPU信息
|
||||||
|
func (s *SysUtils) getCPUInfoPlatform(info *CPUInfo) {
|
||||||
|
file, err := os.Open("/proc/cpuinfo")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
if strings.HasPrefix(line, "model name") {
|
||||||
|
parts := strings.Split(line, ":")
|
||||||
|
if len(parts) > 1 {
|
||||||
|
info.ModelName = strings.TrimSpace(parts[1])
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMemoryInfoPlatform 获取Linux下的内存信息
|
||||||
|
func (s *SysUtils) getMemoryInfoPlatform(info *MemoryInfo) {
|
||||||
|
file, err := os.Open("/proc/meminfo")
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
for scanner.Scan() {
|
||||||
|
line := scanner.Text()
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) < 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
value, err := strconv.ParseUint(fields[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
value *= 1024 // Convert KB to bytes
|
||||||
|
|
||||||
|
switch fields[0] {
|
||||||
|
case "MemTotal:":
|
||||||
|
info.Total = value
|
||||||
|
case "MemAvailable:":
|
||||||
|
info.Available = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Total > 0 && info.Available > 0 {
|
||||||
|
info.Used = info.Total - info.Available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDiskUsagePlatform Linux平台获取磁盘信息
|
||||||
|
func (s *SysUtils) getDiskUsagePlatform(path string) (*DiskInfo, error) {
|
||||||
|
var stat syscall.Statfs_t
|
||||||
|
err := syscall.Statfs(path, &stat)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("获取磁盘信息失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
total := stat.Blocks * uint64(stat.Bsize)
|
||||||
|
free := stat.Bavail * uint64(stat.Bsize)
|
||||||
|
used := total - free
|
||||||
|
usedPct := float64(used) / float64(total) * 100
|
||||||
|
|
||||||
|
return &DiskInfo{
|
||||||
|
Path: path,
|
||||||
|
Total: total,
|
||||||
|
Free: free,
|
||||||
|
Used: used,
|
||||||
|
UsedPct: usedPct,
|
||||||
|
FileSystem: "ext4", // Linux通常是ext4,这里简化处理
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAllMountPoints 获取所有挂载点
|
||||||
|
func (s *SysUtils) getAllMountPoints() ([]string, error) {
|
||||||
|
// 读取/proc/mounts文件
|
||||||
|
content, err := os.ReadFile("/proc/mounts")
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("读取挂载点失败: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var mountPoints []string
|
||||||
|
lines := strings.Split(string(content), "\n")
|
||||||
|
|
||||||
|
for _, line := range lines {
|
||||||
|
fields := strings.Fields(line)
|
||||||
|
if len(fields) >= 2 {
|
||||||
|
mountPoint := fields[1]
|
||||||
|
// 过滤掉一些虚拟文件系统
|
||||||
|
if !strings.HasPrefix(mountPoint, "/proc") &&
|
||||||
|
!strings.HasPrefix(mountPoint, "/sys") &&
|
||||||
|
!strings.HasPrefix(mountPoint, "/dev") &&
|
||||||
|
mountPoint != "/" {
|
||||||
|
mountPoints = append(mountPoints, mountPoint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mountPoints, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAllDisksInfoPlatform Linux平台获取所有磁盘信息
|
||||||
|
func (s *SysUtils) getAllDisksInfoPlatform() ([]*DiskInfo, error) {
|
||||||
|
var disks []*DiskInfo
|
||||||
|
|
||||||
|
// 通常检查根目录和常见挂载点
|
||||||
|
commonPaths := []string{"/", "/home", "/var", "/tmp"}
|
||||||
|
|
||||||
|
for _, path := range commonPaths {
|
||||||
|
if _, err := os.Stat(path); err == nil {
|
||||||
|
diskInfo, err := s.GetDiskUsage(path)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
disks = append(disks, diskInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 也可以尝试从挂载点获取
|
||||||
|
mountPoints, err := s.getAllMountPoints()
|
||||||
|
if err == nil {
|
||||||
|
for _, mount := range mountPoints {
|
||||||
|
diskInfo, err := s.GetDiskUsage(mount)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// 检查是否已经存在
|
||||||
|
exists := false
|
||||||
|
for _, d := range disks {
|
||||||
|
if d.Path == diskInfo.Path {
|
||||||
|
exists = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !exists {
|
||||||
|
disks = append(disks, diskInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return disks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGPUInfoPlatform 获取Linux下的GPU信息
|
||||||
|
func (s *SysUtils) getGPUInfoPlatform() []string {
|
||||||
|
var gpus []string
|
||||||
|
|
||||||
|
// 尝试使用lspci获取GPU信息
|
||||||
|
cmd := exec.Command("lspci")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return gpus
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.Contains(strings.ToLower(line), "vga") ||
|
||||||
|
strings.Contains(strings.ToLower(line), "3d") ||
|
||||||
|
strings.Contains(strings.ToLower(line), "display") {
|
||||||
|
gpus = append(gpus, strings.TrimSpace(line))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpus
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSystemUptime 获取Linux系统启动时间
|
||||||
|
func (s *SysUtils) getSystemUptime() time.Duration {
|
||||||
|
file, err := os.Open("/proc/uptime")
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
var uptime float64
|
||||||
|
fmt.Fscanf(file, "%f", &uptime)
|
||||||
|
return time.Duration(uptime) * time.Second
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSysUtils(t *testing.T) {
|
||||||
|
sys_u := NewSysUtils()
|
||||||
|
sys_u.PrintSystemInfo()
|
||||||
|
}
|
||||||
@@ -0,0 +1,166 @@
|
|||||||
|
//go:build windows
|
||||||
|
// +build windows
|
||||||
|
|
||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
// getCPUInfoPlatform 获取Windows下的CPU信息
|
||||||
|
func (s *SysUtils) getCPUInfoPlatform(info *CPUInfo) {
|
||||||
|
cmd := exec.Command("wmic", "cpu", "get", "name", "/value")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "Name=") {
|
||||||
|
info.ModelName = strings.TrimSpace(strings.TrimPrefix(line, "Name="))
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getMemoryInfoPlatform 获取Windows下的内存信息
|
||||||
|
func (s *SysUtils) getMemoryInfoPlatform(info *MemoryInfo) {
|
||||||
|
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
globalMemoryStatusEx := kernel32.NewProc("GlobalMemoryStatusEx")
|
||||||
|
|
||||||
|
type memoryStatusEx struct {
|
||||||
|
dwLength uint32
|
||||||
|
dwMemoryLoad uint32
|
||||||
|
ullTotalPhys uint64
|
||||||
|
ullAvailPhys uint64
|
||||||
|
ullTotalPageFile uint64
|
||||||
|
ullAvailPageFile uint64
|
||||||
|
ullTotalVirtual uint64
|
||||||
|
ullAvailVirtual uint64
|
||||||
|
ullAvailExtendedVirtual uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
var memStatus memoryStatusEx
|
||||||
|
memStatus.dwLength = uint32(unsafe.Sizeof(memStatus))
|
||||||
|
|
||||||
|
ret, _, _ := globalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&memStatus)))
|
||||||
|
if ret != 0 {
|
||||||
|
info.Total = memStatus.ullTotalPhys
|
||||||
|
info.Available = memStatus.ullAvailPhys
|
||||||
|
info.Used = info.Total - info.Available
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDiskUsagePlatform Windows平台获取磁盘信息
|
||||||
|
func (s *SysUtils) getDiskUsagePlatform(path string) (*DiskInfo, error) {
|
||||||
|
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
getDiskFreeSpaceEx := kernel32.NewProc("GetDiskFreeSpaceExW")
|
||||||
|
|
||||||
|
var freeBytesAvailable uint64
|
||||||
|
var totalNumberOfBytes uint64
|
||||||
|
var totalNumberOfFreeBytes uint64
|
||||||
|
|
||||||
|
pathPtr, _ := syscall.UTF16PtrFromString(path)
|
||||||
|
ret, _, _ := getDiskFreeSpaceEx.Call(
|
||||||
|
uintptr(unsafe.Pointer(pathPtr)),
|
||||||
|
uintptr(unsafe.Pointer(&freeBytesAvailable)),
|
||||||
|
uintptr(unsafe.Pointer(&totalNumberOfBytes)),
|
||||||
|
uintptr(unsafe.Pointer(&totalNumberOfFreeBytes)),
|
||||||
|
)
|
||||||
|
|
||||||
|
if ret == 0 {
|
||||||
|
return nil, fmt.Errorf("获取磁盘信息失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
used := totalNumberOfBytes - totalNumberOfFreeBytes
|
||||||
|
usedPct := float64(used) / float64(totalNumberOfBytes) * 100
|
||||||
|
|
||||||
|
return &DiskInfo{
|
||||||
|
Path: path,
|
||||||
|
Total: totalNumberOfBytes,
|
||||||
|
Free: totalNumberOfFreeBytes,
|
||||||
|
Used: used,
|
||||||
|
UsedPct: usedPct,
|
||||||
|
FileSystem: "NTFS", // Windows通常是NTFS,这里简化处理
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAllDrivesWindows 获取所有磁盘驱动器列表
|
||||||
|
func (s *SysUtils) getAllDrivesWindows() ([]string, error) {
|
||||||
|
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
getLogicalDrives := kernel32.NewProc("GetLogicalDrives")
|
||||||
|
|
||||||
|
ret, _, _ := getLogicalDrives.Call()
|
||||||
|
if ret == 0 {
|
||||||
|
return nil, fmt.Errorf("获取驱动器列表失败")
|
||||||
|
}
|
||||||
|
|
||||||
|
var drives []string
|
||||||
|
for i := 0; i < 26; i++ {
|
||||||
|
if ret&(1<<uint(i)) != 0 {
|
||||||
|
drive := string(rune('A'+i)) + ":\\"
|
||||||
|
drives = append(drives, drive)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return drives, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getAllDisksInfoPlatform Windows平台获取所有磁盘信息
|
||||||
|
func (s *SysUtils) getAllDisksInfoPlatform() ([]*DiskInfo, error) {
|
||||||
|
var disks []*DiskInfo
|
||||||
|
|
||||||
|
drives, err := s.getAllDrivesWindows()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, drive := range drives {
|
||||||
|
diskInfo, err := s.GetDiskUsage(drive)
|
||||||
|
if err != nil {
|
||||||
|
continue // 跳过无法访问的驱动器
|
||||||
|
}
|
||||||
|
disks = append(disks, diskInfo)
|
||||||
|
}
|
||||||
|
|
||||||
|
return disks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// getGPUInfoPlatform 获取Windows下的GPU信息
|
||||||
|
func (s *SysUtils) getGPUInfoPlatform() []string {
|
||||||
|
var gpus []string
|
||||||
|
|
||||||
|
// 使用wmic获取GPU信息
|
||||||
|
cmd := exec.Command("wmic", "path", "win32_VideoController", "get", "name", "/value")
|
||||||
|
output, err := cmd.Output()
|
||||||
|
if err != nil {
|
||||||
|
return gpus
|
||||||
|
}
|
||||||
|
|
||||||
|
lines := strings.Split(string(output), "\n")
|
||||||
|
for _, line := range lines {
|
||||||
|
if strings.HasPrefix(line, "Name=") && len(line) > 5 {
|
||||||
|
gpu := strings.TrimSpace(strings.TrimPrefix(line, "Name="))
|
||||||
|
if gpu != "" {
|
||||||
|
gpus = append(gpus, gpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return gpus
|
||||||
|
}
|
||||||
|
|
||||||
|
// getSystemUptime 获取Windows系统启动时间
|
||||||
|
func (s *SysUtils) getSystemUptime() time.Duration {
|
||||||
|
kernel32 := syscall.NewLazyDLL("kernel32.dll")
|
||||||
|
getTickCount64 := kernel32.NewProc("GetTickCount64")
|
||||||
|
|
||||||
|
ret, _, _ := getTickCount64.Call()
|
||||||
|
return time.Duration(ret) * time.Millisecond
|
||||||
|
}
|
||||||