github.com/zhongdalu/gf@v1.0.0/g/os/gfsnotify/gfsnotify_watcher.go (about) 1 // Copyright 2018 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved. 2 // 3 // This Source Code Form is subject to the terms of the MIT License. 4 // If a copy of the MIT was not distributed with this file, 5 // You can obtain one at https://github.com/zhongdalu/gf. 6 7 package gfsnotify 8 9 import ( 10 "errors" 11 "fmt" 12 13 "github.com/zhongdalu/gf/g/container/glist" 14 ) 15 16 // 添加监控,path参数支持文件或者目录路径,recursive为非必需参数,默认为递归监控(当path为目录时)。 17 // 如果添加目录,这里只会返回目录的callback,按照callback删除时会递归删除。 18 func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { 19 // 首先添加这个文件/目录 20 callback, err = w.addWithCallbackFunc(path, callbackFunc, recursive...) 21 if err != nil { 22 return nil, err 23 } 24 // 如果需要递归,那么递归添加其下的子级目录, 25 // 注意!! 26 // 1、这里只递归添加**目录**, 而非文件,因为监控了目录即监控了其下一级的文件; 27 // 2、这里只是添加底层监控对象对**子级所有目录**的监控,没有任何回调函数的设置,在事件产生时会回溯查找父级的回调函数; 28 if fileIsDir(path) && (len(recursive) == 0 || recursive[0]) { 29 for _, subPath := range fileAllDirs(path) { 30 if fileIsDir(subPath) { 31 w.watcher.Add(subPath) 32 } 33 } 34 } 35 return 36 } 37 38 // 添加对指定文件/目录的监听,并给定回调函数 39 func (w *Watcher) addWithCallbackFunc(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) { 40 // 这里统一转换为当前系统的绝对路径,便于统一监控文件名称 41 if t := fileRealPath(path); t == "" { 42 return nil, errors.New(fmt.Sprintf(`"%s" does not exist`, path)) 43 } else { 44 path = t 45 } 46 callback = &Callback{ 47 Id: callbackIdGenerator.Add(1), 48 Func: callbackFunc, 49 Path: path, 50 recursive: true, 51 } 52 if len(recursive) > 0 { 53 callback.recursive = recursive[0] 54 } 55 // 注册回调函数 56 w.callbacks.LockFunc(func(m map[string]interface{}) { 57 list := (*glist.List)(nil) 58 if v, ok := m[path]; !ok { 59 list = glist.New() 60 m[path] = list 61 } else { 62 list = v.(*glist.List) 63 } 64 callback.elem = list.PushBack(callback) 65 }) 66 // 添加底层监听 67 w.watcher.Add(path) 68 // 添加成功后会注册该callback id到全局的哈希表 69 callbackIdMap.Set(callback.Id, callback) 70 return 71 } 72 73 // 关闭监听管理对象 74 func (w *Watcher) Close() { 75 w.events.Close() 76 w.watcher.Close() 77 close(w.closeChan) 78 } 79 80 // 递归移除对指定文件/目录的所有监听回调 81 func (w *Watcher) Remove(path string) error { 82 // 首先移除path注册的回调注册,以及callbackIdMap中的ID 83 if r := w.callbacks.Remove(path); r != nil { 84 list := r.(*glist.List) 85 for { 86 if r := list.PopFront(); r != nil { 87 callbackIdMap.Remove(r.(*Callback).Id) 88 } else { 89 break 90 } 91 } 92 } 93 // 其次递归判断所有的子级是否可删除监听 94 if subPaths, err := fileScanDir(path, "*", true); err == nil && len(subPaths) > 0 { 95 for _, subPath := range subPaths { 96 if w.checkPathCanBeRemoved(subPath) { 97 w.watcher.Remove(subPath) 98 } 99 } 100 } 101 // 最后移除底层的监听 102 return w.watcher.Remove(path) 103 } 104 105 // 判断给定的路径是否可以删除监听(只有所有回调函数都没有了才能删除) 106 func (w *Watcher) checkPathCanBeRemoved(path string) bool { 107 // 首先检索path对应的回调函数 108 if v := w.callbacks.Get(path); v != nil { 109 return false 110 } 111 // 其次查找父级目录有无回调注册 112 dirPath := fileDir(path) 113 if v := w.callbacks.Get(dirPath); v != nil { 114 return false 115 } 116 // 最后回溯查找递归回调函数 117 for { 118 parentDirPath := fileDir(dirPath) 119 if parentDirPath == dirPath { 120 break 121 } 122 if v := w.callbacks.Get(parentDirPath); v != nil { 123 return false 124 } 125 dirPath = parentDirPath 126 } 127 return true 128 } 129 130 // 根据指定的回调函数ID,移出指定的inotify回调函数 131 func (w *Watcher) RemoveCallback(callbackId int) { 132 callback := (*Callback)(nil) 133 if r := callbackIdMap.Get(callbackId); r != nil { 134 callback = r.(*Callback) 135 } 136 if callback != nil { 137 if r := w.callbacks.Get(callback.Path); r != nil { 138 r.(*glist.List).Remove(callback.elem) 139 } 140 callbackIdMap.Remove(callbackId) 141 } 142 }