github.com/gogf/gf@v1.16.9/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/gogf/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/gogf/gf/os/gfile" 17 "github.com/gogf/gf/os/gfsnotify" 18 "github.com/gogf/gf/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 its all sub files/directories recursively. 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 } else { 79 //fmt.Errorf(err.Error()) 80 } 81 } 82 } 83 84 // addMonitorByPath adds gfsnotify monitoring recursively. 85 // When the files under the directory are updated, the cache will be updated meanwhile. 86 // Note that since the listener is added recursively, if you delete a directory, the files (including the directory) 87 // under the directory will also generate delete events, which means it will generate N+1 events in total 88 // if a directory deleted and there're N files under it. 89 func (sp *SPath) addMonitorByPath(path string) { 90 if sp.cache == nil { 91 return 92 } 93 _, _ = gfsnotify.Add(path, func(event *gfsnotify.Event) { 94 //glog.Debug(event.String()) 95 switch { 96 case event.IsRemove(): 97 sp.cache.Remove(sp.nameFromPath(event.Path, path)) 98 99 case event.IsRename(): 100 if !gfile.Exists(event.Path) { 101 sp.cache.Remove(sp.nameFromPath(event.Path, path)) 102 } 103 104 case event.IsCreate(): 105 sp.addToCache(event.Path, path) 106 } 107 }, true) 108 } 109 110 // removeMonitorByPath removes gfsnotify monitoring of <path> recursively. 111 func (sp *SPath) removeMonitorByPath(path string) { 112 if sp.cache == nil { 113 return 114 } 115 _ = gfsnotify.Remove(path) 116 }