github.com/wangyougui/gf/v2@v2.6.5/os/gspath/gspath_cache.go (about) 1 // Copyright GoFrame Author(https://goframe.org). 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/wangyougui/gf. 6 7 // Package gspath implements file index and search for folders. 8 // 9 10 package gspath 11 12 import ( 13 "runtime" 14 "strings" 15 16 "github.com/wangyougui/gf/v2/os/gfile" 17 "github.com/wangyougui/gf/v2/os/gfsnotify" 18 "github.com/wangyougui/gf/v2/text/gstr" 19 ) 20 21 // updateCacheByPath adds all files under `path` recursively. 22 func (sp *SPath) updateCacheByPath(path string) { 23 if sp.cache == nil { 24 return 25 } 26 sp.addToCache(path, path) 27 } 28 29 // formatCacheName formats `name` with following rules: 30 // 1. The separator is unified to char '/'. 31 // 2. The name should be started with '/' (similar as HTTP URI). 32 func (sp *SPath) formatCacheName(name string) string { 33 if runtime.GOOS != "linux" { 34 name = gstr.Replace(name, "\\", "/") 35 } 36 return "/" + strings.Trim(name, "./") 37 } 38 39 // nameFromPath converts `filePath` to cache name. 40 func (sp *SPath) nameFromPath(filePath, rootPath string) string { 41 name := gstr.Replace(filePath, rootPath, "") 42 name = sp.formatCacheName(name) 43 return name 44 } 45 46 // makeCacheValue formats `filePath` to cache value. 47 func (sp *SPath) makeCacheValue(filePath string, isDir bool) string { 48 if isDir { 49 return filePath + "_D_" 50 } 51 return filePath + "_F_" 52 } 53 54 // parseCacheValue parses cache value to file path and type. 55 func (sp *SPath) parseCacheValue(value string) (filePath string, isDir bool) { 56 if value[len(value)-2 : len(value)-1][0] == 'F' { 57 return value[:len(value)-3], false 58 } 59 return value[:len(value)-3], true 60 } 61 62 // addToCache adds an item to cache. 63 // If `filePath` is a directory, it also adds its all sub files/directories recursively 64 // to the cache. 65 func (sp *SPath) addToCache(filePath, rootPath string) { 66 // Add itself firstly. 67 idDir := gfile.IsDir(filePath) 68 sp.cache.SetIfNotExist( 69 sp.nameFromPath(filePath, rootPath), sp.makeCacheValue(filePath, idDir), 70 ) 71 // If it's a directory, it adds all of its sub files/directories. 72 if idDir { 73 if files, err := gfile.ScanDir(filePath, "*", true); err == nil { 74 // fmt.Println("gspath add to cache:", filePath, files) 75 for _, path := range files { 76 sp.cache.SetIfNotExist(sp.nameFromPath(path, rootPath), sp.makeCacheValue(path, gfile.IsDir(path))) 77 } 78 } 79 } 80 } 81 82 // addMonitorByPath adds gfsnotify monitoring recursively. 83 // When the files under the directory are updated, the cache will be updated meanwhile. 84 // Note that since the listener is added recursively, if you delete a directory, the files (including the directory) 85 // under the directory will also generate delete events, which means it will generate N+1 events in total 86 // if a directory deleted and there're N files under it. 87 func (sp *SPath) addMonitorByPath(path string) { 88 if sp.cache == nil { 89 return 90 } 91 _, _ = gfsnotify.Add(path, func(event *gfsnotify.Event) { 92 // glog.Debug(event.String()) 93 switch { 94 case event.IsRemove(): 95 sp.cache.Remove(sp.nameFromPath(event.Path, path)) 96 97 case event.IsRename(): 98 if !gfile.Exists(event.Path) { 99 sp.cache.Remove(sp.nameFromPath(event.Path, path)) 100 } 101 102 case event.IsCreate(): 103 sp.addToCache(event.Path, path) 104 } 105 }, true) 106 } 107 108 // removeMonitorByPath removes gfsnotify monitoring of `path` recursively. 109 func (sp *SPath) removeMonitorByPath(path string) { 110 if sp.cache == nil { 111 return 112 } 113 _ = gfsnotify.Remove(path) 114 }