// Package mover 负责文件的移动,跨文件系统时自动回退为复制后删除。 package mover import ( "fmt" "io" "os" "path/filepath" "strings" "github.com/redmask-hb/GoSimplePrint/goPrint" uuid "github.com/satori/go.uuid" ) // Mover 文件移动器 type Mover struct{} // MoveToDir 将一批文件移动到目标目录(目标文件名使用 uuid 前缀防止同名覆盖)。 // 返回移动成功的 源路径 → 目标目录 映射,供记录去向使用。 func (m *Mover) MoveToDir(filePaths []string, targetDir string) map[string]string { moved := make(map[string]string) if len(filePaths) == 0 { return moved } if err := os.MkdirAll(targetDir, 0755); err != nil { fmt.Printf("=== 创建目录失败: %s, 错误: %v\n", targetDir, err) return moved } bar := goPrint.NewBar(100) bar.SetNotice("=== 移动文件到目录:") bar.SetGraph(">") total := len(filePaths) for i, src := range filePaths { if moveOne(src, targetDir) { moved[src] = targetDir } bar.PrintBar(percent(i+1, total)) } bar.PrintEnd("=== Finish") return moved } // moveOne 移动单个文件;跨文件系统时 fallback 为 copy+delete func moveOne(src, targetDir string) bool { fileName := strings.ReplaceAll(uuid.NewV4().String(), "-", "") + "_" + filepath.Base(src) target := filepath.Join(targetDir, fileName) if err := os.Rename(src, target); err == nil { return true } if err := copyFile(src, target); err != nil { fmt.Printf("=== 复制文件失败, 源: %s, 目标: %s, 错误: %v\n", src, target, err) return false } if err := os.Remove(src); err != nil { fmt.Printf("=== 删除源文件失败: %s, 错误: %v\n", src, err) // 复制成功但删除失败,文件已存在于两处,视为移动成功 } return true } // copyFile 逐块复制文件;失败时清理已写入的目标文件,避免残留半截文件 func copyFile(src, dst string) error { in, err := os.Open(src) if err != nil { return err } defer in.Close() out, err := os.Create(dst) if err != nil { return err } if _, err := io.Copy(out, in); err != nil { out.Close() _ = os.Remove(dst) return err } if err := out.Close(); err != nil { _ = os.Remove(dst) return err } return nil } // percent 计算百分比(0~100) func percent(part, total int) int { if total <= 0 { return 100 } return int(float64(part) / float64(total) * 100) }