package main import ( "context" "fmt" "os" "path/filepath" "github.com/fsnotify/fsnotify" ) func WatchDirectory(ctx context.Context, path string, op ...fsnotify.Op) (<-chan string, error) { watcher, err := fsnotify.NewWatcher() if err != nil { return nil, err } ops := fsnotify.Op(0) for _, op := range op { ops |= op } err = watcher.Add(path) if err != nil { return nil, err } files, err := os.ReadDir(path) if err != nil { return nil, err } ch := make(chan string, 1000) // close channel on app shutdown go func() { <-ctx.Done() watcher.Close() }() go func(watcher *fsnotify.Watcher, ch chan<- string) { defer watcher.Close() defer close(ch) for _, file := range files { path := filepath.Join(path, file.Name()) if file.IsDir() { //fmt.Println("watching directory", path) watcher.Add(path) if dirFiles, err := os.ReadDir(path); err == nil { for _, dirFile := range dirFiles { ch <- filepath.Join(path, dirFile.Name()) } } } else { ch <- path } } for { select { case event, ok := <-watcher.Events: if !ok { close(ch) return } if fi, err := os.Stat(event.Name); err == nil && fi.IsDir() { if event.Op&fsnotify.Create == fsnotify.Create { //fmt.Println("watching directory", event.Name) watcher.Add(event.Name) } // read dir immediately because directory files could change simultanously with its parent directory if dirFiles, err := os.ReadDir(event.Name); err == nil { for _, dirFile := range dirFiles { ch <- filepath.Join(event.Name, dirFile.Name()) } } } else if err != nil && event.Op&fsnotify.Remove == fsnotify.Remove { //fmt.Println("stopped watching directory", event.Name) watcher.Remove(event.Name) } if event.Op&ops > 0 { ch <- event.Name } case err, ok := <-watcher.Errors: if ok { fmt.Println(err, ok) } close(ch) return } } }(watcher, ch) return ch, nil }