github.com/gogf/gf@v1.16.9/os/gres/gres_resource.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 gres 8 9 import ( 10 "context" 11 "fmt" 12 "github.com/gogf/gf/internal/intlog" 13 "os" 14 "path/filepath" 15 "strings" 16 17 "github.com/gogf/gf/os/gtime" 18 19 "github.com/gogf/gf/container/gtree" 20 "github.com/gogf/gf/os/gfile" 21 ) 22 23 type Resource struct { 24 tree *gtree.BTree 25 } 26 27 const ( 28 gDEFAULT_TREE_M = 100 29 ) 30 31 // New creates and returns a new resource object. 32 func New() *Resource { 33 return &Resource{ 34 tree: gtree.NewBTree(gDEFAULT_TREE_M, func(v1, v2 interface{}) int { 35 return strings.Compare(v1.(string), v2.(string)) 36 }), 37 } 38 } 39 40 // Add unpacks and adds the <content> into current resource object. 41 // The unnecessary parameter <prefix> indicates the prefix 42 // for each file storing into current resource object. 43 func (r *Resource) Add(content string, prefix ...string) error { 44 files, err := UnpackContent(content) 45 if err != nil { 46 intlog.Printf(context.TODO(), "Add resource files failed: %v", err) 47 return err 48 } 49 namePrefix := "" 50 if len(prefix) > 0 { 51 namePrefix = prefix[0] 52 } 53 for i := 0; i < len(files); i++ { 54 files[i].resource = r 55 r.tree.Set(namePrefix+files[i].file.Name, files[i]) 56 } 57 intlog.Printf(context.TODO(), "Add %d files to resource manager", r.tree.Size()) 58 return nil 59 } 60 61 // Load loads, unpacks and adds the data from <path> into current resource object. 62 // The unnecessary parameter <prefix> indicates the prefix 63 // for each file storing into current resource object. 64 func (r *Resource) Load(path string, prefix ...string) error { 65 realPath, err := gfile.Search(path) 66 if err != nil { 67 return err 68 } 69 return r.Add(gfile.GetContents(realPath), prefix...) 70 } 71 72 // Get returns the file with given path. 73 func (r *Resource) Get(path string) *File { 74 if path == "" { 75 return nil 76 } 77 path = strings.Replace(path, "\\", "/", -1) 78 path = strings.Replace(path, "//", "/", -1) 79 if path != "/" { 80 for path[len(path)-1] == '/' { 81 path = path[:len(path)-1] 82 } 83 } 84 result := r.tree.Get(path) 85 if result != nil { 86 return result.(*File) 87 } 88 return nil 89 } 90 91 // GetWithIndex searches file with <path>, if the file is directory 92 // it then does index files searching under this directory. 93 // 94 // GetWithIndex is usually used for http static file service. 95 func (r *Resource) GetWithIndex(path string, indexFiles []string) *File { 96 // Necessary for double char '/' replacement in prefix. 97 path = strings.Replace(path, "\\", "/", -1) 98 path = strings.Replace(path, "//", "/", -1) 99 if path != "/" { 100 for path[len(path)-1] == '/' { 101 path = path[:len(path)-1] 102 } 103 } 104 if file := r.Get(path); file != nil { 105 if len(indexFiles) > 0 && file.FileInfo().IsDir() { 106 var f *File 107 for _, name := range indexFiles { 108 if f = r.Get(path + "/" + name); f != nil { 109 return f 110 } 111 } 112 } 113 return file 114 } 115 return nil 116 } 117 118 // GetContent directly returns the content of <path>. 119 func (r *Resource) GetContent(path string) []byte { 120 file := r.Get(path) 121 if file != nil { 122 return file.Content() 123 } 124 return nil 125 } 126 127 // Contains checks whether the <path> exists in current resource object. 128 func (r *Resource) Contains(path string) bool { 129 return r.Get(path) != nil 130 } 131 132 // IsEmpty checks and returns whether the resource manager is empty. 133 func (r *Resource) IsEmpty() bool { 134 return r.tree.IsEmpty() 135 } 136 137 // ScanDir returns the files under the given path, the parameter <path> should be a folder type. 138 // 139 // The pattern parameter <pattern> supports multiple file name patterns, 140 // using the ',' symbol to separate multiple patterns. 141 // 142 // It scans directory recursively if given parameter <recursive> is true. 143 // 144 // Note that the returned files does not contain given parameter <path>. 145 func (r *Resource) ScanDir(path string, pattern string, recursive ...bool) []*File { 146 isRecursive := false 147 if len(recursive) > 0 { 148 isRecursive = recursive[0] 149 } 150 return r.doScanDir(path, pattern, isRecursive, false) 151 } 152 153 // ScanDirFile returns all sub-files with absolute paths of given <path>, 154 // It scans directory recursively if given parameter <recursive> is true. 155 // 156 // Note that it returns only files, exclusive of directories. 157 func (r *Resource) ScanDirFile(path string, pattern string, recursive ...bool) []*File { 158 isRecursive := false 159 if len(recursive) > 0 { 160 isRecursive = recursive[0] 161 } 162 return r.doScanDir(path, pattern, isRecursive, true) 163 } 164 165 // doScanDir is an internal method which scans directory 166 // and returns the absolute path list of files that are not sorted. 167 // 168 // The pattern parameter <pattern> supports multiple file name patterns, 169 // using the ',' symbol to separate multiple patterns. 170 // 171 // It scans directory recursively if given parameter <recursive> is true. 172 func (r *Resource) doScanDir(path string, pattern string, recursive bool, onlyFile bool) []*File { 173 path = strings.Replace(path, "\\", "/", -1) 174 path = strings.Replace(path, "//", "/", -1) 175 if path != "/" { 176 for path[len(path)-1] == '/' { 177 path = path[:len(path)-1] 178 } 179 } 180 var ( 181 name = "" 182 files = make([]*File, 0) 183 length = len(path) 184 patterns = strings.Split(pattern, ",") 185 ) 186 for i := 0; i < len(patterns); i++ { 187 patterns[i] = strings.TrimSpace(patterns[i]) 188 } 189 // Used for type checking for first entry. 190 first := true 191 r.tree.IteratorFrom(path, true, func(key, value interface{}) bool { 192 if first { 193 if !value.(*File).FileInfo().IsDir() { 194 return false 195 } 196 first = false 197 } 198 if onlyFile && value.(*File).FileInfo().IsDir() { 199 return true 200 } 201 name = key.(string) 202 if len(name) <= length { 203 return true 204 } 205 if path != name[:length] { 206 return false 207 } 208 // To avoid of, eg: /i18n and /i18n-dir 209 if !first && name[length] != '/' { 210 return true 211 } 212 if !recursive { 213 if strings.IndexByte(name[length+1:], '/') != -1 { 214 return true 215 } 216 } 217 for _, p := range patterns { 218 if match, err := filepath.Match(p, gfile.Basename(name)); err == nil && match { 219 files = append(files, value.(*File)) 220 return true 221 } 222 } 223 return true 224 }) 225 return files 226 } 227 228 // Dump prints the files of current resource object. 229 func (r *Resource) Dump() { 230 var info os.FileInfo 231 r.tree.Iterator(func(key, value interface{}) bool { 232 info = value.(*File).FileInfo() 233 fmt.Printf("%v %7s %s\n", gtime.New(info.ModTime()).ISO8601(), gfile.FormatSize(info.Size()), key) 234 return true 235 }) 236 fmt.Printf("TOTAL FILES: %d\n", r.tree.Size()) 237 }