2018-04-17 18:58:22 +08:00
|
|
|
|
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
|
|
|
|
//
|
|
|
|
|
// This Source Code Form is subject to the terms of the MIT License.
|
|
|
|
|
// If a copy of the MIT was not distributed with this file,
|
|
|
|
|
// You can obtain one at https://gitee.com/johng/gf.
|
|
|
|
|
|
|
|
|
|
// 文件监控.
|
2018-04-18 16:00:47 +08:00
|
|
|
|
// 使用时需要注意的是,一旦一个文件被删除,那么对其的监控将会失效。
|
2018-04-17 18:58:22 +08:00
|
|
|
|
package gfsnotify
|
|
|
|
|
|
|
|
|
|
import (
|
2018-04-18 09:16:08 +08:00
|
|
|
|
"errors"
|
2018-05-02 17:13:35 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/glog"
|
2018-10-22 11:13:00 +08:00
|
|
|
|
"gitee.com/johng/gf/third/github.com/fsnotify/fsnotify"
|
2018-04-18 09:16:08 +08:00
|
|
|
|
"gitee.com/johng/gf/g/os/gfile"
|
2018-04-17 18:58:22 +08:00
|
|
|
|
"gitee.com/johng/gf/g/container/gmap"
|
|
|
|
|
"gitee.com/johng/gf/g/container/glist"
|
2018-04-18 15:17:07 +08:00
|
|
|
|
"gitee.com/johng/gf/g/container/gqueue"
|
2018-09-28 22:39:15 +08:00
|
|
|
|
"fmt"
|
2018-04-17 18:58:22 +08:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 监听管理对象
|
|
|
|
|
type Watcher struct {
|
2018-05-17 22:32:50 +08:00
|
|
|
|
watcher *fsnotify.Watcher // 底层fsnotify对象
|
|
|
|
|
events *gqueue.Queue // 过滤后的事件通知,不会出现重复事件
|
|
|
|
|
closeChan chan struct{} // 关闭事件
|
|
|
|
|
callbacks *gmap.StringInterfaceMap // 监听的回调函数
|
2018-04-17 18:58:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 监听事件对象
|
|
|
|
|
type Event struct {
|
2018-04-18 09:16:08 +08:00
|
|
|
|
Path string // 文件绝对路径
|
2018-04-17 18:58:22 +08:00
|
|
|
|
Op Op // 触发监听的文件操作
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 按位进行识别的操作集合
|
|
|
|
|
type Op uint32
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
CREATE Op = 1 << iota
|
|
|
|
|
WRITE
|
|
|
|
|
REMOVE
|
|
|
|
|
RENAME
|
|
|
|
|
CHMOD
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// 全局监听对象,方便应用端调用
|
2018-08-19 11:28:51 +08:00
|
|
|
|
var watcher, _ = New()
|
2018-04-18 15:17:07 +08:00
|
|
|
|
|
2018-04-17 18:58:22 +08:00
|
|
|
|
// 创建监听管理对象
|
2018-08-19 11:28:51 +08:00
|
|
|
|
func New() (*Watcher, error) {
|
2018-04-17 18:58:22 +08:00
|
|
|
|
if watch, err := fsnotify.NewWatcher(); err == nil {
|
|
|
|
|
w := &Watcher {
|
2018-05-17 22:32:50 +08:00
|
|
|
|
watcher : watch,
|
|
|
|
|
events : gqueue.New(),
|
2018-09-19 09:47:50 +08:00
|
|
|
|
closeChan : make(chan struct{}),
|
2018-05-17 22:32:50 +08:00
|
|
|
|
callbacks : gmap.NewStringInterfaceMap(),
|
2018-04-17 18:58:22 +08:00
|
|
|
|
}
|
|
|
|
|
w.startWatchLoop()
|
2018-04-18 15:17:07 +08:00
|
|
|
|
w.startEventLoop()
|
2018-04-17 18:58:22 +08:00
|
|
|
|
return w, nil
|
|
|
|
|
} else {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-19 11:25:15 +08:00
|
|
|
|
// 添加对指定文件/目录的监听,并给定回调函数;如果给定的是一个目录,默认递归监控。
|
2018-09-28 22:39:15 +08:00
|
|
|
|
func Add(path string, callback func(event *Event), recursive...bool) error {
|
2018-08-19 11:25:15 +08:00
|
|
|
|
if watcher == nil {
|
|
|
|
|
return errors.New("global watcher creating failed")
|
|
|
|
|
}
|
2018-09-28 22:39:15 +08:00
|
|
|
|
return watcher.Add(path, callback, recursive...)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 移除监听,默认递归删除。
|
|
|
|
|
func Remove(path string) error {
|
|
|
|
|
if watcher == nil {
|
|
|
|
|
return errors.New("global watcher creating failed")
|
|
|
|
|
}
|
2018-08-19 11:28:51 +08:00
|
|
|
|
return watcher.Remove(path)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
2018-04-17 18:58:22 +08:00
|
|
|
|
// 关闭监听管理对象
|
|
|
|
|
func (w *Watcher) Close() {
|
|
|
|
|
w.watcher.Close()
|
2018-04-18 15:17:07 +08:00
|
|
|
|
w.events.Close()
|
2018-09-19 09:47:50 +08:00
|
|
|
|
close(w.closeChan)
|
2018-04-17 18:58:22 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 添加对指定文件/目录的监听,并给定回调函数
|
2018-08-19 11:28:51 +08:00
|
|
|
|
func (w *Watcher) addWatch(path string, callback func(event *Event)) error {
|
2018-05-02 17:13:35 +08:00
|
|
|
|
// 这里统一转换为当前系统的绝对路径,便于统一监控文件名称
|
2018-05-02 17:16:29 +08:00
|
|
|
|
t := gfile.RealPath(path)
|
|
|
|
|
if t == "" {
|
2018-09-28 22:39:15 +08:00
|
|
|
|
return errors.New(fmt.Sprintf(`"%s" does not exist`, path))
|
2018-04-17 18:58:22 +08:00
|
|
|
|
}
|
2018-05-02 17:16:29 +08:00
|
|
|
|
path = t
|
2018-04-17 18:58:22 +08:00
|
|
|
|
// 注册回调函数
|
|
|
|
|
w.callbacks.LockFunc(func(m map[string]interface{}) {
|
|
|
|
|
var result interface{}
|
|
|
|
|
if v, ok := m[path]; !ok {
|
|
|
|
|
result = glist.New()
|
|
|
|
|
m[path] = result
|
|
|
|
|
} else {
|
|
|
|
|
result = v
|
|
|
|
|
}
|
|
|
|
|
result.(*glist.List).PushBack(callback)
|
|
|
|
|
})
|
|
|
|
|
// 添加底层监听
|
|
|
|
|
w.watcher.Add(path)
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-09-28 22:39:15 +08:00
|
|
|
|
// 添加监控,path参数支持文件或者目录路径,recursive为非必需参数,默认为递归添加监控(当path为目录时)
|
|
|
|
|
func (w *Watcher) Add(path string, callback func(event *Event), recursive...bool) error {
|
|
|
|
|
if gfile.IsDir(path) && (len(recursive) == 0 || recursive[0]) {
|
2018-09-13 18:40:04 +08:00
|
|
|
|
paths, _ := gfile.ScanDir(path, "*", true)
|
|
|
|
|
list := []string{path}
|
|
|
|
|
list = append(list, paths...)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
for _, v := range list {
|
2018-08-19 11:28:51 +08:00
|
|
|
|
if err := w.addWatch(v, callback); err != nil {
|
2018-08-19 11:25:15 +08:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
} else {
|
2018-08-19 11:28:51 +08:00
|
|
|
|
return w.addWatch(path, callback)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2018-04-18 09:16:08 +08:00
|
|
|
|
// 移除监听
|
2018-08-19 11:28:51 +08:00
|
|
|
|
func (w *Watcher) removeWatch(path string) error {
|
2018-04-18 09:16:08 +08:00
|
|
|
|
w.callbacks.Remove(path)
|
|
|
|
|
return w.watcher.Remove(path)
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-19 11:25:15 +08:00
|
|
|
|
// 递归移除监听
|
2018-08-19 11:28:51 +08:00
|
|
|
|
func (w *Watcher) Remove(path string) error {
|
2018-08-19 11:25:15 +08:00
|
|
|
|
if gfile.IsDir(path) {
|
2018-09-13 18:40:04 +08:00
|
|
|
|
paths, _ := gfile.ScanDir(path, "*", true)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
list := []string{path}
|
2018-09-13 18:40:04 +08:00
|
|
|
|
list = append(list, paths...)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
for _, v := range list {
|
2018-08-19 11:28:51 +08:00
|
|
|
|
if err := w.removeWatch(v); err != nil {
|
2018-08-19 11:25:15 +08:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
} else {
|
2018-08-19 11:28:51 +08:00
|
|
|
|
return w.removeWatch(path)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2018-05-17 22:32:50 +08:00
|
|
|
|
// 监听循环
|
2018-04-17 18:58:22 +08:00
|
|
|
|
func (w *Watcher) startWatchLoop() {
|
|
|
|
|
go func() {
|
|
|
|
|
for {
|
|
|
|
|
select {
|
|
|
|
|
// 关闭事件
|
|
|
|
|
case <- w.closeChan:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
// 监听事件
|
|
|
|
|
case ev := <- w.watcher.Events:
|
2018-10-27 16:05:36 +08:00
|
|
|
|
//glog.Debug("gfsnotify: watch loop", ev)
|
2018-09-15 16:40:13 +08:00
|
|
|
|
w.events.Push(&Event{
|
2018-04-18 09:16:08 +08:00
|
|
|
|
Path : ev.Name,
|
2018-04-17 18:58:22 +08:00
|
|
|
|
Op : Op(ev.Op),
|
2018-04-18 15:17:07 +08:00
|
|
|
|
})
|
2018-04-17 18:58:22 +08:00
|
|
|
|
|
2018-05-02 17:13:35 +08:00
|
|
|
|
case err := <- w.watcher.Errors:
|
|
|
|
|
glog.Error("error : ", err);
|
2018-04-17 18:58:22 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-04-18 15:17:07 +08:00
|
|
|
|
}()
|
|
|
|
|
}
|
|
|
|
|
|
2018-08-19 11:25:15 +08:00
|
|
|
|
// 检索给定path的回调方法**列表**
|
|
|
|
|
func (w *Watcher) getCallbacks(path string) *glist.List {
|
|
|
|
|
for path != "/" {
|
|
|
|
|
if l := w.callbacks.Get(path); l != nil {
|
|
|
|
|
return l.(*glist.List)
|
|
|
|
|
} else {
|
|
|
|
|
path = gfile.Dir(path)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2018-04-18 15:17:07 +08:00
|
|
|
|
// 事件循环
|
|
|
|
|
func (w *Watcher) startEventLoop() {
|
|
|
|
|
go func() {
|
|
|
|
|
for {
|
2018-09-15 16:40:13 +08:00
|
|
|
|
if v := w.events.Pop(); v != nil {
|
2018-10-27 16:05:36 +08:00
|
|
|
|
//glog.Debug("gfsnotidy: event loop", v)
|
2018-04-18 15:17:07 +08:00
|
|
|
|
event := v.(*Event)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
if event.IsRemove() {
|
|
|
|
|
if gfile.Exists(event.Path) {
|
|
|
|
|
// 如果是文件删除事件,判断该文件是否存在,如果存在,那么将此事件认为“假删除”,
|
|
|
|
|
// 并重新添加监控(底层fsnotify会自动删除掉监控,这里重新添加回去)
|
|
|
|
|
w.watcher.Add(event.Path)
|
2018-10-27 16:05:36 +08:00
|
|
|
|
// 修改时间操作为写入
|
|
|
|
|
event.Op = WRITE
|
2018-08-19 11:25:15 +08:00
|
|
|
|
} else {
|
|
|
|
|
// 如果是真实删除,那么递归删除监控信息
|
2018-08-19 11:28:51 +08:00
|
|
|
|
w.Remove(event.Path)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2018-10-27 16:05:36 +08:00
|
|
|
|
//glog.Debug("gfsnotidy: event loop callbacks", v)
|
2018-08-19 11:25:15 +08:00
|
|
|
|
callbacks := w.getCallbacks(event.Path)
|
|
|
|
|
// 如果创建了新的目录,那么将这个目录递归添加到监控中
|
|
|
|
|
if event.IsCreate() && gfile.IsDir(event.Path) {
|
|
|
|
|
for _, callback := range callbacks.FrontAll() {
|
2018-08-19 11:28:51 +08:00
|
|
|
|
w.Add(event.Path, callback.(func(event *Event)))
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}
|
2018-04-18 16:00:47 +08:00
|
|
|
|
}
|
2018-08-19 11:25:15 +08:00
|
|
|
|
if callbacks != nil {
|
|
|
|
|
go func(callbacks *glist.List) {
|
|
|
|
|
for _, callback := range callbacks.FrontAll() {
|
|
|
|
|
callback.(func(event *Event))(event)
|
2018-04-18 15:17:07 +08:00
|
|
|
|
}
|
2018-08-19 11:25:15 +08:00
|
|
|
|
}(callbacks)
|
2018-04-18 15:17:07 +08:00
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
break
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}()
|
2018-04-17 18:58:22 +08:00
|
|
|
|
}
|