github.com/webx-top/com@v1.2.12/monitor.go (about)

     1  /*
     2  
     3     Copyright 2016 Wenhui Shen <www.webx.top>
     4  
     5     Licensed under the Apache License, Version 2.0 (the "License");
     6     you may not use this file except in compliance with the License.
     7     You may obtain a copy of the License at
     8  
     9         http://www.apache.org/licenses/LICENSE-2.0
    10  
    11     Unless required by applicable law or agreed to in writing, software
    12     distributed under the License is distributed on an "AS IS" BASIS,
    13     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14     See the License for the specific language governing permissions and
    15     limitations under the License.
    16  
    17  */
    18  
    19  package com
    20  
    21  import (
    22  	"errors"
    23  	"log"
    24  	"os"
    25  	"path/filepath"
    26  	"sync"
    27  
    28  	"github.com/admpub/fsnotify"
    29  )
    30  
    31  var (
    32  	DefaultMonitor      = NewMonitor()
    33  	MonitorEventEmptyFn = func(string) {}
    34  )
    35  
    36  func NewMonitor() *MonitorEvent {
    37  	return &MonitorEvent{
    38  		Create:  MonitorEventEmptyFn,
    39  		Delete:  MonitorEventEmptyFn,
    40  		Modify:  MonitorEventEmptyFn,
    41  		Chmod:   MonitorEventEmptyFn,
    42  		Rename:  MonitorEventEmptyFn,
    43  		filters: []func(string) bool{},
    44  	}
    45  }
    46  
    47  //MonitorEvent 监控事件函数
    48  type MonitorEvent struct {
    49  	//文件事件
    50  	Create func(string) //创建
    51  	Delete func(string) //删除(包含文件夹和文件。因为已经删除,无法确定是文件夹还是文件)
    52  	Modify func(string) //修改(包含修改权限。如果是文件夹,则内部的文件被更改也会触发此事件)
    53  	Chmod  func(string) //修改权限(windows不支持)
    54  	Rename func(string) //重命名
    55  
    56  	//其它
    57  	Channel chan bool //管道
    58  	Debug   bool
    59  	watcher *fsnotify.Watcher
    60  	filters []func(string) bool
    61  	lock    sync.RWMutex
    62  }
    63  
    64  func (m *MonitorEvent) AddFilter(args ...func(string) bool) *MonitorEvent {
    65  	if m.filters == nil {
    66  		m.filters = []func(string) bool{}
    67  	}
    68  	m.filters = append(m.filters, args...)
    69  	return m
    70  }
    71  
    72  func (m *MonitorEvent) SetFilters(args ...func(string) bool) *MonitorEvent {
    73  	m.filters = args
    74  	return m
    75  }
    76  
    77  func (m *MonitorEvent) Watch(args ...func(string) bool) *MonitorEvent {
    78  	m.SetFilters(args...)
    79  	go func() {
    80  		m.backendListen()
    81  		<-m.Channel
    82  	}()
    83  	return m
    84  }
    85  
    86  func (m *MonitorEvent) Close() error {
    87  	if m.Channel != nil {
    88  		close(m.Channel)
    89  	}
    90  	if m.watcher != nil {
    91  		return m.watcher.Close()
    92  	}
    93  	return nil
    94  }
    95  
    96  func (m *MonitorEvent) Watcher() *fsnotify.Watcher {
    97  	m.lock.Lock()
    98  	defer m.lock.Unlock()
    99  	if m.watcher == nil {
   100  		var err error
   101  		m.watcher, err = fsnotify.NewWatcher()
   102  		m.Channel = make(chan bool)
   103  		m.filters = []func(string) bool{}
   104  		if err != nil {
   105  			log.Panic(err)
   106  		}
   107  	}
   108  	return m.watcher
   109  }
   110  
   111  func (m *MonitorEvent) backendListen() *MonitorEvent {
   112  	go m.listen()
   113  	return m
   114  }
   115  
   116  func (m *MonitorEvent) AddDir(dir string) error {
   117  	f, err := os.Stat(dir)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	if !f.IsDir() {
   122  		return errors.New(dir + ` is not dir.`)
   123  	}
   124  	err = filepath.Walk(dir, func(f string, info os.FileInfo, err error) error {
   125  		if err != nil {
   126  			return err
   127  		}
   128  		if info.IsDir() {
   129  			if m.Debug {
   130  				log.Println(`[Monitor]`, `Add Watch:`, f)
   131  			}
   132  			return m.Watcher().Add(f)
   133  		}
   134  		return nil
   135  	})
   136  	return err
   137  }
   138  
   139  func (m *MonitorEvent) AddFile(file string) error {
   140  	if m.Debug {
   141  		log.Println(`[Monitor]`, `Add Watch:`, file)
   142  	}
   143  	return m.Watcher().Add(file)
   144  }
   145  
   146  func (m *MonitorEvent) Remove(fileOrDir string) error {
   147  	if m.watcher != nil {
   148  		return m.watcher.Remove(fileOrDir)
   149  	}
   150  	return nil
   151  }
   152  
   153  func (m *MonitorEvent) listen() {
   154  	for {
   155  		watcher := m.Watcher()
   156  		select {
   157  		case ev, ok := <-watcher.Events:
   158  			if !ok {
   159  				return
   160  			}
   161  			if m.Debug {
   162  				log.Println(`[Monitor]`, `Trigger Event:`, ev)
   163  			}
   164  			if m.filters != nil {
   165  				var skip bool
   166  				for _, filter := range m.filters {
   167  					if !filter(ev.Name) {
   168  						skip = true
   169  						break
   170  					}
   171  				}
   172  				if skip {
   173  					break
   174  				}
   175  			}
   176  			switch {
   177  			case ev.Op&fsnotify.Create == fsnotify.Create:
   178  				if m.IsDir(ev.Name) {
   179  					watcher.Add(ev.Name)
   180  				}
   181  				if m.Create != nil {
   182  					m.Create(ev.Name)
   183  				}
   184  			case ev.Op&fsnotify.Remove == fsnotify.Remove:
   185  				if m.IsDir(ev.Name) {
   186  					watcher.Remove(ev.Name)
   187  				}
   188  				if m.Delete != nil {
   189  					m.Delete(ev.Name)
   190  				}
   191  			case ev.Op&fsnotify.Write == fsnotify.Write:
   192  				if m.Modify != nil {
   193  					m.Modify(ev.Name)
   194  				}
   195  			case ev.Op&fsnotify.Rename == fsnotify.Rename:
   196  				watcher.Remove(ev.Name)
   197  				if m.Rename != nil {
   198  					m.Rename(ev.Name)
   199  				}
   200  			case ev.Op&fsnotify.Chmod == fsnotify.Chmod:
   201  				if m.Chmod != nil {
   202  					m.Chmod(ev.Name)
   203  				}
   204  			}
   205  		case err, ok := <-watcher.Errors:
   206  			if !ok {
   207  				return
   208  			}
   209  			if err != nil {
   210  				log.Println("Watcher error:", err)
   211  			}
   212  		}
   213  	}
   214  }
   215  
   216  func (m *MonitorEvent) IsDir(path string) bool {
   217  	d, e := os.Stat(path)
   218  	if e != nil {
   219  		return false
   220  	}
   221  	return d.IsDir()
   222  }
   223  
   224  //Monitor 文件监测
   225  func Monitor(rootDir string, callback *MonitorEvent, args ...func(string) bool) error {
   226  	watcher := callback.Watcher()
   227  	defer watcher.Close()
   228  	callback.Watch(args...)
   229  	err := callback.AddDir(rootDir)
   230  	if err != nil {
   231  		callback.Close()
   232  		return err
   233  	}
   234  	return nil
   235  }