package tool import ( "crypto/md5" "fmt" "io" "os" "path/filepath" ) // FileMD5 calculate file md5 // - if file not exist, return "" // - if _path is dir, return "" func FileMD5(_path string) string { // 检查文件是否存在 fileInfo, err := os.Stat(_path) if err != nil { if os.IsNotExist(err) { return "" } // 其他错误也返回空字符串 return "" } // 检查是否是目录 if fileInfo.IsDir() { return "" } // 打开文件 file, err := os.Open(_path) if err != nil { return "" } defer file.Close() // 创建MD5哈希计算器 hash := md5.New() // 将文件内容复制到哈希计算器 if _, err := io.Copy(hash, file); err != nil { return "" } // 计算并返回MD5哈希值(十六进制字符串) return fmt.Sprintf("%x", hash.Sum(nil)) } // CopyFile copies a file from src to dst. // Returns an error if source/destination are the same, source isn't a regular file, // or any step in the copy process fails. func CopyFile(src, dst string) error { // Open source file srcFile, err := os.Open(src) if err != nil { return fmt.Errorf("failed to open source: %w", err) } defer srcFile.Close() // Get source file metadata srcInfo, err := srcFile.Stat() if err != nil { return fmt.Errorf("failed to get source info: %w", err) } // Verify source is a regular file if !srcInfo.Mode().IsRegular() { return fmt.Errorf("source is not a regular file") } // Check if source and destination are the same file if same, err := sameFile(src, dst, srcInfo); same { return fmt.Errorf("source and destination are the same file") } else if err != nil { return err } // Create destination directory structure if err := os.MkdirAll(filepath.Dir(dst), 0755); err != nil { return fmt.Errorf("failed to create destination directory: %w", err) } // Create destination file with source permissions dstFile, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, srcInfo.Mode()) if err != nil { return fmt.Errorf("failed to create destination: %w", err) } // Copy contents and handle destination close errors _, err = io.Copy(dstFile, srcFile) if closeErr := dstFile.Close(); closeErr != nil && err == nil { err = fmt.Errorf("failed to close destination: %w", closeErr) } if err != nil { return fmt.Errorf("copy failed: %w", err) } return nil } // sameFile checks if src and dst refer to the same file using device/inode numbers func sameFile(src, dst string, srcInfo os.FileInfo) (bool, error) { dstInfo, err := os.Stat(dst) if os.IsNotExist(err) { return false, nil // Destination doesn't exist } if err != nil { return false, err // Other errors } return os.SameFile(srcInfo, dstInfo), nil } func CopyDir(src, dst string) error { // todo: copy src dir to dst dir recursively // if dst is not exist, create it // if file exist, overwrite it srcInfo, err := os.Stat(src) if err != nil { return fmt.Errorf("stat src dir failed: %w", err) } if !srcInfo.IsDir() { return fmt.Errorf("source is not a directory") } // Create destination directory if it does not exist if err := os.MkdirAll(dst, srcInfo.Mode()); err != nil { return fmt.Errorf("failed to create destination directory: %w", err) } entries, err := os.ReadDir(src) if err != nil { return fmt.Errorf("failed to read source directory: %w", err) } for _, entry := range entries { srcPath := filepath.Join(src, entry.Name()) dstPath := filepath.Join(dst, entry.Name()) info, err := entry.Info() if err != nil { return fmt.Errorf("failed to get info for %s: %w", srcPath, err) } if info.IsDir() { // Recursively copy subdirectory if err := CopyDir(srcPath, dstPath); err != nil { return err } } else { // Copy file, overwrite if exists if err := CopyFile(srcPath, dstPath); err != nil { return err } } } return nil }