1 package main 2 3 import ( 4 "crypto/md5" 5 "fmt" 6 "go-admin/log" 7 "os" 8 "path/filepath" 9 "time" 10 11 "github.com/fsnotify/fsnotify" 12 ) 13 14 type FileChangeCallback func(absfpname string) 15 16 type ConfigFileWatcher struct { 17 WatchedDirectory string // 监控目录 18 FileSuffix string // 监控的文件类型 19 OnFileChange FileChangeCallback // 相关文件有变更就会调用这个回调 20 } 21 22 func (cfw ConfigFileWatcher) WatchConfigFileChanges() { 23 // 注意如果该文件被其他程序占用的话会读取不出内容的。比如vscode打开了文件 24 getFileHash := func(filename string) (string, error) { 25 data, err := os.ReadFile(filename) 26 if err != nil { 27 return "", err 28 } 29 return fmt.Sprintf("%x", md5.Sum(data)), nil 30 } 31 32 watcher, err := fsnotify.NewWatcher() 33 if err != nil { 34 log.Fatal("WatchConfigFileChanges", err.Error(), "") 35 } 36 defer watcher.Close() 37 38 dir := cfw.WatchedDirectory 39 40 fileInfos := make(map[string]string) 41 42 err = filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { 43 if err != nil { 44 return err 45 } 46 if info.IsDir() { 47 err = watcher.Add(path) 48 if err != nil { 49 log.Fatal("WatchConfigFileChanges", err.Error(), "") 50 } 51 } else { 52 hash, err := getFileHash(path) 53 if err != nil { 54 log.Fatal("WatchConfigFileChanges", "获取文件哈希出错:%s", err.Error()) 55 } else { 56 fileInfos[path] = hash 57 } 58 } 59 return nil 60 }) 61 if err != nil { 62 log.Fatal("WatchConfigFileChanges", "%s", err.Error()) 63 64 } 65 66 fmt.Println("开始监控...") 67 68 var lastEventTime time.Time 69 interval := 500 * time.Millisecond // 设置等待时间为500毫秒 70 71 func() { 72 for { 73 select { 74 case event, ok := <-watcher.Events: 75 if !ok { 76 return 77 } 78 if (event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create) && 79 filepath.Ext(event.Name) == cfw.FileSuffix { 80 81 // 检查时间间隔,如果在等待时间内则跳过 82 if time.Since(lastEventTime) < interval { 83 continue 84 } 85 86 // 获取文件的最新MD5值 87 newHash, err := getFileHash(event.Name) 88 if err != nil { 89 log.Fatal("WatchConfigFileChanges", "获取文件哈希出错:%s", err.Error()) 90 } else { 91 // 对比MD5值,确认文件内容是否发生了变化 92 if prevHash, exists := fileInfos[event.Name]; exists && prevHash == newHash { 93 fmt.Println("文件未更改:", event.Name) 94 log.Log("WatchConfigFileChanges", "文件未更改:%s", event.Name) 95 } else { 96 fileInfos[event.Name] = newHash 97 absPath, _ := filepath.Abs(event.Name) 98 log.Log("WatchConfigFileChanges", "文件修改或创建:%s", absPath) 99 cfw.OnFileChange(absPath) 100 } 101 } 102 103 // 更新最后事件时间 104 lastEventTime = time.Now() 105 106 // 因为fsnotify源码常量定义错了。Rename和Remove搞反了 107 } else if (event.Op&fsnotify.Rename == fsnotify.Rename) && filepath.Ext(event.Name) == cfw.FileSuffix { 108 // 检查时间间隔,如果在等待时间内则跳过 109 if time.Since(lastEventTime) < interval { 110 continue 111 } 112 absPath, _ := filepath.Abs(event.Name) 113 fmt.Println("文件被删除:", absPath) 114 cfw.OnFileChange(absPath) 115 // 更新最后事件时间 116 lastEventTime = time.Now() 117 } 118 case err, ok := <-watcher.Errors: 119 if !ok { 120 return 121 } 122 log.Fatal("WatchConfigFileChanges", "错误:%s", err.Error()) 123 } 124 } 125 }() 126 127 }