Files
gf/g/os/gfsnotify/gfsnotify.go

227 lines
6.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 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.
// 文件监控.
// 使用时需要注意的是,一旦一个文件被删除,那么对其的监控将会失效。
package gfsnotify
import (
"errors"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/third/github.com/fsnotify/fsnotify"
"gitee.com/johng/gf/g/os/gfile"
"gitee.com/johng/gf/g/container/gmap"
"gitee.com/johng/gf/g/container/glist"
"gitee.com/johng/gf/g/container/gqueue"
"fmt"
)
// 监听管理对象
type Watcher struct {
watcher *fsnotify.Watcher // 底层fsnotify对象
events *gqueue.Queue // 过滤后的事件通知,不会出现重复事件
closeChan chan struct{} // 关闭事件
callbacks *gmap.StringInterfaceMap // 监听的回调函数
}
// 监听事件对象
type Event struct {
Path string // 文件绝对路径
Op Op // 触发监听的文件操作
}
// 按位进行识别的操作集合
type Op uint32
const (
CREATE Op = 1 << iota
WRITE
REMOVE
RENAME
CHMOD
)
// 全局监听对象,方便应用端调用
var watcher, _ = New()
// 创建监听管理对象
func New() (*Watcher, error) {
if watch, err := fsnotify.NewWatcher(); err == nil {
w := &Watcher {
watcher : watch,
events : gqueue.New(),
closeChan : make(chan struct{}),
callbacks : gmap.NewStringInterfaceMap(),
}
w.startWatchLoop()
w.startEventLoop()
return w, nil
} else {
return nil, err
}
}
// 添加对指定文件/目录的监听,并给定回调函数;如果给定的是一个目录,默认递归监控。
func Add(path string, callback func(event *Event), recursive...bool) error {
if watcher == nil {
return errors.New("global watcher creating failed")
}
return watcher.Add(path, callback, recursive...)
}
// 移除监听,默认递归删除。
func Remove(path string) error {
if watcher == nil {
return errors.New("global watcher creating failed")
}
return watcher.Remove(path)
}
// 关闭监听管理对象
func (w *Watcher) Close() {
w.watcher.Close()
w.events.Close()
close(w.closeChan)
}
// 添加对指定文件/目录的监听,并给定回调函数
func (w *Watcher) addWatch(path string, callback func(event *Event)) error {
// 这里统一转换为当前系统的绝对路径,便于统一监控文件名称
t := gfile.RealPath(path)
if t == "" {
return errors.New(fmt.Sprintf(`"%s" does not exist`, path))
}
path = t
// 注册回调函数
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
}
// 添加监控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]) {
paths, _ := gfile.ScanDir(path, "*", true)
list := []string{path}
list = append(list, paths...)
for _, v := range list {
if err := w.addWatch(v, callback); err != nil {
return err
}
}
return nil
} else {
return w.addWatch(path, callback)
}
}
// 移除监听
func (w *Watcher) removeWatch(path string) error {
w.callbacks.Remove(path)
return w.watcher.Remove(path)
}
// 递归移除监听
func (w *Watcher) Remove(path string) error {
if gfile.IsDir(path) {
paths, _ := gfile.ScanDir(path, "*", true)
list := []string{path}
list = append(list, paths...)
for _, v := range list {
if err := w.removeWatch(v); err != nil {
return err
}
}
return nil
} else {
return w.removeWatch(path)
}
}
// 监听循环
func (w *Watcher) startWatchLoop() {
go func() {
for {
select {
// 关闭事件
case <- w.closeChan:
return
// 监听事件
case ev := <- w.watcher.Events:
//glog.Debug("gfsnotify:", ev)
w.events.Push(&Event{
Path : ev.Name,
Op : Op(ev.Op),
})
case err := <- w.watcher.Errors:
glog.Error("error : ", err);
}
}
}()
}
// 检索给定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
}
// 事件循环
func (w *Watcher) startEventLoop() {
go func() {
for {
if v := w.events.Pop(); v != nil {
event := v.(*Event)
if event.IsRemove() {
if gfile.Exists(event.Path) {
// 如果是文件删除事件,判断该文件是否存在,如果存在,那么将此事件认为“假删除”,
// 并重新添加监控(底层fsnotify会自动删除掉监控这里重新添加回去)
w.watcher.Add(event.Path)
continue
} else {
// 如果是真实删除,那么递归删除监控信息
w.Remove(event.Path)
}
}
callbacks := w.getCallbacks(event.Path)
// 如果创建了新的目录,那么将这个目录递归添加到监控中
if event.IsCreate() && gfile.IsDir(event.Path) {
for _, callback := range callbacks.FrontAll() {
w.Add(event.Path, callback.(func(event *Event)))
}
}
if callbacks != nil {
go func(callbacks *glist.List) {
for _, callback := range callbacks.FrontAll() {
callback.(func(event *Event))(event)
}
}(callbacks)
}
} else {
break
}
}
}()
}