gres_resource.go 6.2 KB

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