package hash import ( "crypto/md5" "crypto/sha256" "encoding/binary" "encoding/hex" "hash" "io" "os" "sysmonitord/pkg/logger" "go.uber.org/zap" ) type HashAlgorithm interface { Hash() hash.Hash Name() string } // ==== SHA256 ==== type SHA256Algorithm struct{} func (a *SHA256Algorithm) Hash() hash.Hash { return sha256.New() } func (a *SHA256Algorithm) Name() string { return "sha256" } // ==== MD5 ==== type MD5Algorithm struct{} func (a *MD5Algorithm) Hash() hash.Hash { return md5.New() } func (a *MD5Algorithm) Name() string { return "md5" } // ==== xxHash64 ==== // Todo: 添加 xxHash64 实现 // ==== 配置结构体 ==== type Config struct { UseFastHash bool Threshold int64 ChunkSize int64 Algorithm HashAlgorithm } // ==== 计算文件哈希 ==== func Calculate(filePath string, fileSize int64, cfg *Config) (string, error) { if cfg == nil { cfg = &Config{ Algorithm: &SHA256Algorithm{}, } } if fileSize == 0 { info, err := os.Stat(filePath) if err != nil { logger.Log.Warn("[scanner]获取文件信息失败", zap.String("path", filePath), zap.Error(err)) return "", err } fileSize = info.Size() } if cfg.Algorithm == nil { cfg.Algorithm = &SHA256Algorithm{} } logger.Log.Debug("[scanner]计算文件哈希", zap.String("path", filePath), zap.Int64("size", fileSize), zap.String("algorithm", cfg.Algorithm.Name())) if cfg.UseFastHash && fileSize > cfg.Threshold { return calculateFast(filePath, fileSize, cfg) } else { return calculateFull(filePath, cfg) } } func calculateFull(filePath string, cfg *Config) (string, error) { file, err := os.Open(filePath) if err != nil { logger.Log.Warn("[scanner]打开文件失败", zap.String("path", filePath), zap.Error(err)) return "", err } defer file.Close() hasher := cfg.Algorithm.Hash() if _, err := io.Copy(hasher, file); err != nil { logger.Log.Error("[scanner]读取文件失败", zap.String("path", filePath), zap.Error(err)) return "", err } hashBytes := hasher.Sum(nil) hashString := hex.EncodeToString(hashBytes) return hashString, nil } func calculateFast(filePath string, fileSize int64, cfg *Config) (string, error) { file, err := os.Open(filePath) if err != nil { logger.Log.Warn("[scanner]打开文件失败", zap.String("path", filePath), zap.Error(err)) return "", err } defer file.Close() hasher := cfg.Algorithm.Hash() chunkSize := cfg.ChunkSize if _, err := io.CopyN(hasher, file, chunkSize); err != nil { if err != io.EOF { return "", err } } tailOffset := fileSize - chunkSize if tailOffset < 0 { tailOffset = 0 } if _, err := file.Seek(tailOffset, io.SeekStart); err != nil { return "", err } if _, err := io.CopyN(hasher, file, chunkSize); err != nil { return "", err } if err := binary.Write(hasher, binary.BigEndian, fileSize); err != nil { return "", err } return hex.EncodeToString(hasher.Sum(nil)), nil }