Op 的方法

String

  1. var ops = map[Op]string{
  2. Create: "CREATE",
  3. Write: "WRITE",
  4. Remove: "REMOVE",
  5. Rename: "RENAME",
  6. Chmod: "CHMOD",
  7. Move: "MOVE",
  8. }
  9. // String prints the string version of the Op consts
  10. func (e Op) String() string {
  11. if op, found := ops[e]; found {
  12. return op
  13. }
  14. return "???"
  15. }

对 ops 中的六种操作返回对应的 string ,对于没有的就返回 “???”

Event 的方法

String

  1. // An Event describes an event that is received when files or directory
  2. // changes occur. It includes the os.FileInfo of the changed file or
  3. // directory and the type of event that's occurred and the full path of the file.
  4. type Event struct {
  5. Op
  6. Path string
  7. os.FileInfo
  8. }
  9. // String returns a string depending on what type of event occurred and the
  10. // file name associated with the event.
  11. func (e Event) String() string {
  12. if e.FileInfo != nil {
  13. pathType := "FILE"
  14. if e.IsDir() {
  15. pathType = "DIRECTORY"
  16. }
  17. return fmt.Sprintf("%s %q %s [%s]", pathType, e.Name(), e.Op, e.Path)
  18. }
  19. return "???"
  20. }

也是返回 Event 对应的字符串

Watcher 的方法

SetMaxEvents

  1. // SetMaxEvents controls the maximum amount of events that are sent on
  2. // the Event channel per watching cycle. If max events is less than 1, there is
  3. // no limit, which is the default.
  4. func (w *Watcher) SetMaxEvents(delta int) {
  5. w.mu.Lock()
  6. w.maxEvents = delta
  7. w.mu.Unlock()
  8. }

这里的 mu 是互斥锁,可以保证同一时间只有一个 goroutine 在修改 maxEvents 值,后面很多方法也都这样做了。
这个方法设置了此 Watcher 每次有改动时 print 出来的事件的最大数量,比如设置为 1 ,然后在 Watching 的目录下新建一个文件,会有这样的结果
Methods analysis - 图1
只打印出了一条信息,但如果不设置限制或者限制大于1,则
Methods analysis - 图2
推荐保持默认,如有特殊需求则自行测试

IgnoreHiddenFiles

  1. // IgnoreHiddenFiles sets the watcher to ignore any file or directory
  2. // that starts with a dot.
  3. func (w *Watcher) IgnoreHiddenFiles(ignore bool) {
  4. w.mu.Lock()
  5. w.ignoreHidden = ignore
  6. w.mu.Unlock()
  7. }

顾名思义,设置是否忽略隐藏文件
隐藏文件即文件名第一个字符是.的文件,也就是作者所说的 dotFile ,如.dotFile
选择隐藏后就不会将隐藏文件纳入 Watching 列表

FilterOps

  1. // FilterOps filters which event op types should be returned
  2. // when an event occurs.
  3. func (w *Watcher) FilterOps(ops ...Op) {
  4. w.mu.Lock()
  5. w.ops = make(map[Op]struct{})
  6. for _, op := range ops {
  7. w.ops[op] = struct{}{}
  8. }
  9. w.mu.Unlock()
  10. }

选择要监视的操作,即 Op 那六个常量操作中的一个或多个
进行 Watching ,有某个操作触发了,会先检查这个操作是否被监测,如果没有被监测,就跳过,如果被监测,再打印出这个操作的信息
如果不进行设置,就默认监测全部六个操作

Ignore

  1. // Ignore adds paths that should be ignored.
  2. //
  3. // For files that are already added, Ignore removes them.
  4. func (w *Watcher) Ignore(paths ...string) (err error) {
  5. for _, path := range paths {
  6. path, err = filepath.Abs(path)
  7. if err != nil {
  8. return err
  9. }
  10. // Remove any of the paths that were already added.
  11. if err := w.RemoveRecursive(path); err != nil {
  12. return err
  13. }
  14. w.mu.Lock()
  15. w.ignored[path] = struct{}{}
  16. w.mu.Unlock()
  17. }
  18. return nil
  19. }

将参数中的路径对应的文件或文件夹加入忽略名单,忽略名单中的文件及文件夹不会被监测

List

  1. func (w *Watcher) list(name string) (map[string]os.FileInfo, error) {
  2. fileList := make(map[string]os.FileInfo)
  3. // Make sure name exists.
  4. stat, err := os.Stat(name)
  5. if err != nil {
  6. return nil, err
  7. }
  8. fileList[name] = stat
  9. // If it's not a directory, just return.
  10. if !stat.IsDir() {
  11. return fileList, nil
  12. }
  13. // It's a directory.
  14. fInfoList, err := ioutil.ReadDir(name)
  15. if err != nil {
  16. return nil, err
  17. }
  18. // Add all of the files in the directory to the file list as long
  19. // as they aren't on the ignored list or are hidden files if ignoreHidden
  20. // is set to true.
  21. for _, fInfo := range fInfoList {
  22. path := filepath.Join(name, fInfo.Name())
  23. _, ignored := w.ignored[path]
  24. if ignored || (w.ignoreHidden && strings.HasPrefix(fInfo.Name(), ".")) {
  25. continue
  26. }
  27. fileList[path] = fInfo
  28. }
  29. return fileList, nil
  30. }

返回指定路径下的所有文件夹和文件的一个 map 表,忽略列表中的不会加入 map 中

ListRecursive

  1. func (w *Watcher) listRecursive(name string) (map[string]os.FileInfo, error) {
  2. fileList := make(map[string]os.FileInfo)
  3. return fileList, filepath.Walk(name, func(path string, info os.FileInfo, err error) error {
  4. if err != nil {
  5. return err
  6. }
  7. // If path is ignored and it's a directory, skip the directory. If it's
  8. // ignored and it's a single file, skip the file.
  9. _, ignored := w.ignored[path]
  10. if ignored || (w.ignoreHidden && strings.HasPrefix(info.Name(), ".")) {
  11. if info.IsDir() {
  12. return filepath.SkipDir
  13. }
  14. return nil
  15. }
  16. // Add the path and it's info to the file list.
  17. fileList[path] = info
  18. return nil
  19. })
  20. }

跟List方法功能类似,不过是递归的,遇到文件夹会将文件夹中的内容再列出来,直到列完,当然忽略列表中的也不会列出。
作者并没有直接写递归,而是调用了filepath.Walk方法去递归遍历指定路径下的所有文件

Add

  1. // Add adds either a single file or directory to the file list.
  2. func (w *Watcher) Add(name string) (err error) {
  3. w.mu.Lock()
  4. defer w.mu.Unlock()
  5. name, err = filepath.Abs(name)
  6. if err != nil {
  7. return err
  8. }
  9. // If name is on the ignored list or if hidden files are
  10. // ignored and name is a hidden file or directory, simply return.
  11. _, ignored := w.ignored[name]
  12. if ignored || (w.ignoreHidden && strings.HasPrefix(name, ".")) {
  13. return nil
  14. }
  15. // Add the directory's contents to the files list.
  16. fileList, err := w.list(name)
  17. if err != nil {
  18. return err
  19. }
  20. for k, v := range fileList {
  21. w.files[k] = v
  22. }
  23. // Add the name to the names list.
  24. w.names[name] = false
  25. return nil
  26. }

这个方法将参数对应的文件或文件夹加入监测名单,如果在忽略名单中就不加入监测名单,非递归

AddRecursive

  1. // AddRecursive adds either a single file or directory recursively to the file list.
  2. func (w *Watcher) AddRecursive(name string) (err error) {
  3. w.mu.Lock()
  4. defer w.mu.Unlock()
  5. name, err = filepath.Abs(name)
  6. if err != nil {
  7. return err
  8. }
  9. fileList, err := w.listRecursive(name)
  10. if err != nil {
  11. return err
  12. }
  13. for k, v := range fileList {
  14. w.files[k] = v
  15. }
  16. // Add the name to the names list.
  17. w.names[name] = true
  18. return nil
  19. }

这个方法将参数对应的文件或文件夹加入监测名单,如果在忽略名单中就不加入监测名单,递归

Remove

  1. // Remove removes either a single file or directory from the file's list.
  2. func (w *Watcher) Remove(name string) (err error) {
  3. w.mu.Lock()
  4. defer w.mu.Unlock()
  5. name, err = filepath.Abs(name)
  6. if err != nil {
  7. return err
  8. }
  9. // Remove the name from w's names list.
  10. delete(w.names, name)
  11. // If name is a single file, remove it and return.
  12. info, found := w.files[name]
  13. if !found {
  14. return nil // Doesn't exist, just return.
  15. }
  16. if !info.IsDir() {
  17. delete(w.files, name)
  18. return nil
  19. }
  20. // Delete the actual directory from w.files
  21. delete(w.files, name)
  22. // If it's a directory, delete all of it's contents from w.files.
  23. for path := range w.files {
  24. if filepath.Dir(path) == name {
  25. delete(w.files, path)
  26. }
  27. }
  28. return nil
  29. }

移出监测名单,非递归

RemoveRecursive

  1. // RemoveRecursive removes either a single file or a directory recursively from
  2. // the file's list.
  3. func (w *Watcher) RemoveRecursive(name string) (err error) {
  4. w.mu.Lock()
  5. defer w.mu.Unlock()
  6. name, err = filepath.Abs(name)
  7. if err != nil {
  8. return err
  9. }
  10. // Remove the name from w's names list.
  11. delete(w.names, name)
  12. // If name is a single file, remove it and return.
  13. info, found := w.files[name]
  14. if !found {
  15. return nil // Doesn't exist, just return.
  16. }
  17. if !info.IsDir() {
  18. delete(w.files, name)
  19. return nil
  20. }
  21. // If it's a directory, delete all of it's contents recursively
  22. // from w.files.
  23. for path := range w.files {
  24. if strings.HasPrefix(path, name) {
  25. delete(w.files, path)
  26. }
  27. }
  28. return nil
  29. }

移出监测名单,递归

WatchedFiles

  1. // WatchedFiles returns a map of files added to a Watcher.
  2. func (w *Watcher) WatchedFiles() map[string]os.FileInfo {
  3. w.mu.Lock()
  4. defer w.mu.Unlock()
  5. return w.files
  6. }

这个方法返回 Watcher 对象的 files 属性,也就是被检测的文件列表

TriggerEvent

  1. // TriggerEvent is a method that can be used to trigger an event, separate to
  2. // the file watching process.
  3. func (w *Watcher) TriggerEvent(eventType Op, file os.FileInfo) {
  4. w.Wait()
  5. if file == nil {
  6. file = &fileInfo{name: "triggered event", modTime: time.Now()}
  7. }
  8. w.Event <- Event{Op: eventType, Path: "-", FileInfo: file}
  9. }

Trigger ,也就是扳机的意思,这个方法用于触发一个事件,比如 REMOVE 或者 CREATE

retrieveFileList

  1. func (w *Watcher) retrieveFileList() map[string]os.FileInfo {
  2. w.mu.Lock()
  3. defer w.mu.Unlock()
  4. fileList := make(map[string]os.FileInfo)
  5. var list map[string]os.FileInfo
  6. var err error
  7. for name, recursive := range w.names {
  8. if recursive {
  9. list, err = w.listRecursive(name)
  10. if err != nil {
  11. if os.IsNotExist(err) {
  12. w.Error <- ErrWatchedFileDeleted
  13. w.mu.Unlock()
  14. w.RemoveRecursive(name)
  15. w.mu.Lock()
  16. } else {
  17. w.Error <- err
  18. }
  19. }
  20. } else {
  21. list, err = w.list(name)
  22. if err != nil {
  23. if os.IsNotExist(err) {
  24. w.Error <- ErrWatchedFileDeleted
  25. w.mu.Unlock()
  26. w.Remove(name)
  27. w.mu.Lock()
  28. } else {
  29. w.Error <- err
  30. }
  31. }
  32. }
  33. // Add the file's to the file list.
  34. for k, v := range list {
  35. fileList[k] = v
  36. }
  37. }
  38. return fileList
  39. }

这个方法并没有暴露出去,只供内部调用,具体做的事情也很简单,就是去获取被监测的文件及文件夹的当前状态

pollEvents

  1. func (w *Watcher) pollEvents(files map[string]os.FileInfo, evt chan Event,
  2. cancel chan struct{}) {
  3. w.mu.Lock()
  4. defer w.mu.Unlock()
  5. // Store create and remove events for use to check for rename events.
  6. creates := make(map[string]os.FileInfo)
  7. removes := make(map[string]os.FileInfo)
  8. // Check for removed files.
  9. for path, info := range w.files {
  10. if _, found := files[path]; !found {
  11. removes[path] = info
  12. }
  13. }
  14. // Check for created files, writes and chmods.
  15. for path, info := range files {
  16. oldInfo, found := w.files[path]
  17. if !found {
  18. // A file was created.
  19. creates[path] = info
  20. continue
  21. }
  22. if oldInfo.ModTime() != info.ModTime() {
  23. select {
  24. case <-cancel:
  25. return
  26. case evt <- Event{Write, path, info}:
  27. }
  28. }
  29. if oldInfo.Mode() != info.Mode() {
  30. select {
  31. case <-cancel:
  32. return
  33. case evt <- Event{Chmod, path, info}:
  34. }
  35. }
  36. }
  37. // Check for renames and moves.
  38. for path1, info1 := range removes {
  39. for path2, info2 := range creates {
  40. if sameFile(info1, info2) {
  41. e := Event{
  42. Op: Move,
  43. Path: fmt.Sprintf("%s -> %s", path1, path2),
  44. FileInfo: info1,
  45. }
  46. // If they are from the same directory, it's a rename
  47. // instead of a move event.
  48. if filepath.Dir(path1) == filepath.Dir(path2) {
  49. e.Op = Rename
  50. }
  51. delete(removes, path1)
  52. delete(creates, path2)
  53. select {
  54. case <-cancel:
  55. return
  56. case evt <- e:
  57. }
  58. }
  59. }
  60. }
  61. // Send all the remaining create and remove events.
  62. for path, info := range creates {
  63. select {
  64. case <-cancel:
  65. return
  66. case evt <- Event{Create, path, info}:
  67. }
  68. }
  69. for path, info := range removes {
  70. select {
  71. case <-cancel:
  72. return
  73. case evt <- Event{Remove, path, info}:
  74. }
  75. }
  76. }

这个方法也没有暴露,只供 Start 方法调用,功能就是去比较传入的参数中的文件 map 表(也就是当前状态)和 Watcher 对象的 files 属性中存的 map 表(也就是初始状态),如果有不同的地方就讲这个时间放入参数的 Event 通道中

Start

  1. // Start begins the polling cycle which repeats every specified
  2. // duration until Close is called.
  3. func (w *Watcher) Start(d time.Duration) error {
  4. // Return an error if d is less than 1 nanosecond.
  5. if d < time.Nanosecond {
  6. return ErrDurationTooShort
  7. }
  8. // Make sure the Watcher is not already running.
  9. w.mu.Lock()
  10. if w.running {
  11. w.mu.Unlock()
  12. return ErrWatcherRunning
  13. }
  14. w.running = true
  15. w.mu.Unlock()
  16. // Unblock w.Wait().
  17. w.wg.Done()
  18. for {
  19. // done lets the inner polling cycle loop know when the
  20. // current cycle's method has finished executing.
  21. done := make(chan struct{})
  22. // Any events that are found are first piped to evt before
  23. // being sent to the main Event channel.
  24. evt := make(chan Event)
  25. // Retrieve the file list for all watched file's and dirs.
  26. fileList := w.retrieveFileList()
  27. // cancel can be used to cancel the current event polling function.
  28. cancel := make(chan struct{})
  29. // Look for events.
  30. go func() {
  31. w.pollEvents(fileList, evt, cancel)
  32. done <- struct{}{}
  33. }()
  34. // numEvents holds the number of events for the current cycle.
  35. numEvents := 0
  36. inner:
  37. for {
  38. select {
  39. case <-w.close:
  40. close(cancel)
  41. close(w.Closed)
  42. return nil
  43. case event := <-evt:
  44. if len(w.ops) > 0 { // Filter Ops.
  45. _, found := w.ops[event.Op]
  46. if !found {
  47. continue
  48. }
  49. }
  50. numEvents++
  51. if w.maxEvents > 0 && numEvents > w.maxEvents {
  52. close(cancel)
  53. break inner
  54. }
  55. w.Event <- event
  56. case <-done: // Current cycle is finished.
  57. break inner
  58. }
  59. }
  60. // Update the file's list.
  61. w.mu.Lock()
  62. w.files = fileList
  63. w.mu.Unlock()
  64. // Sleep and then continue to the next loop iteration.
  65. time.Sleep(d)
  66. }
  67. }

这就是 Watcher 启动的方法了,而做的事情也很简单,接收一个time.Duration对象作为参数,每隔一段时间(也就是这个 Duration ),进行一次检测,也就是调用 retrieveFiles 读取一次被监测文件的当前状态,然后去跟之前保存的初始状态做对比,出现不同就将这个操作输出

Wait

// Wait blocks until the watcher is started.
func (w *Watcher) Wait() {
    w.wg.Wait()
}

Watcher 对象中的 wg 是一个 sync.WaitGroup 对象,Wait 方法就是去等待 Done 动作,具体可以自己去看 sync.WaitGroup 的包解析

Close

// Close stops a Watcher and unlocks its mutex, then sends a close signal.
func (w *Watcher) Close() {
    w.mu.Lock()
    if !w.running {
        w.mu.Unlock()
        return
    }
    w.running = false
    w.files = make(map[string]os.FileInfo)
    w.names = make(map[string]bool)
    w.mu.Unlock()
    // Send a close signal to the Start method.
    w.close <- struct{}{}
}

很直白,就是关闭一个 Watcher

小结

分析了 Watcher 的所有方法,发现其实做的事情并不复杂,能够把源码都读懂就能很清楚的知道它具体所做的事情了,就是一直去对比当前状态和初始状态的异同,然后抛出不同的事件提示用户
当然只是文字讲可能会比较枯燥,接下来用流程图来解释,可能会更加明白一点