gres_resource.go 6.3 KB

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