Files
cluster/pkg/tool/file.go
loveuer 9780a2b028 feat: add registry config, image upload/download, and OCI format support
Backend:
- Add registry_address configuration API (GET/POST)
- Add tar image upload with OCI and Docker format support
- Add image download with streaming optimization
- Fix blob download using c.Send (Fiber v3 SendStream bug)
- Add registry_address prefix stripping for all OCI v2 endpoints
- Add AGENTS.md for project documentation

Frontend:
- Add settings store with Snackbar notifications
- Add image upload dialog with progress bar
- Add download state tracking with multi-stage feedback
- Replace alert() with MUI Snackbar messages
- Display image names without registry_address prefix

🤖 Generated with [Qoder](https://qoder.com)
2025-11-10 16:28:58 +08:00

158 lines
3.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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
}