gfile_scan.go 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184
  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 gfile
  7. import (
  8. "path/filepath"
  9. "sort"
  10. "github.com/gogf/gf/v2/errors/gerror"
  11. "github.com/gogf/gf/v2/text/gstr"
  12. )
  13. const (
  14. // Max recursive depth for directory scanning.
  15. maxScanDepth = 100000
  16. )
  17. // ScanDir returns all sub-files with absolute paths of given `path`,
  18. // It scans directory recursively if given parameter `recursive` is true.
  19. //
  20. // The pattern parameter `pattern` supports multiple file name patterns,
  21. // using the ',' symbol to separate multiple patterns.
  22. func ScanDir(path string, pattern string, recursive ...bool) ([]string, error) {
  23. isRecursive := false
  24. if len(recursive) > 0 {
  25. isRecursive = recursive[0]
  26. }
  27. list, err := doScanDir(0, path, pattern, isRecursive, nil)
  28. if err != nil {
  29. return nil, err
  30. }
  31. if len(list) > 0 {
  32. sort.Strings(list)
  33. }
  34. return list, nil
  35. }
  36. // ScanDirFunc returns all sub-files with absolute paths of given `path`,
  37. // It scans directory recursively if given parameter `recursive` is true.
  38. //
  39. // The pattern parameter `pattern` supports multiple file name patterns, using the ','
  40. // symbol to separate multiple patterns.
  41. //
  42. // The parameter `recursive` specifies whether scanning the `path` recursively, which
  43. // means it scans its sub-files and appends the files path to result array if the sub-file
  44. // is also a folder. It is false in default.
  45. //
  46. // The parameter `handler` specifies the callback function handling each sub-file path of
  47. // the `path` and its sub-folders. It ignores the sub-file path if `handler` returns an empty
  48. // string, or else it appends the sub-file path to result slice.
  49. func ScanDirFunc(path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) {
  50. list, err := doScanDir(0, path, pattern, recursive, handler)
  51. if err != nil {
  52. return nil, err
  53. }
  54. if len(list) > 0 {
  55. sort.Strings(list)
  56. }
  57. return list, nil
  58. }
  59. // ScanDirFile returns all sub-files with absolute paths of given `path`,
  60. // It scans directory recursively if given parameter `recursive` is true.
  61. //
  62. // The pattern parameter `pattern` supports multiple file name patterns,
  63. // using the ',' symbol to separate multiple patterns.
  64. //
  65. // Note that it returns only files, exclusive of directories.
  66. func ScanDirFile(path string, pattern string, recursive ...bool) ([]string, error) {
  67. isRecursive := false
  68. if len(recursive) > 0 {
  69. isRecursive = recursive[0]
  70. }
  71. list, err := doScanDir(0, path, pattern, isRecursive, func(path string) string {
  72. if IsDir(path) {
  73. return ""
  74. }
  75. return path
  76. })
  77. if err != nil {
  78. return nil, err
  79. }
  80. if len(list) > 0 {
  81. sort.Strings(list)
  82. }
  83. return list, nil
  84. }
  85. // ScanDirFileFunc returns all sub-files with absolute paths of given `path`,
  86. // It scans directory recursively if given parameter `recursive` is true.
  87. //
  88. // The pattern parameter `pattern` supports multiple file name patterns, using the ','
  89. // symbol to separate multiple patterns.
  90. //
  91. // The parameter `recursive` specifies whether scanning the `path` recursively, which
  92. // means it scans its sub-files and appends the file paths to result array if the sub-file
  93. // is also a folder. It is false in default.
  94. //
  95. // The parameter `handler` specifies the callback function handling each sub-file path of
  96. // the `path` and its sub-folders. It ignores the sub-file path if `handler` returns an empty
  97. // string, or else it appends the sub-file path to result slice.
  98. //
  99. // Note that the parameter `path` for `handler` is not a directory but a file.
  100. // It returns only files, exclusive of directories.
  101. func ScanDirFileFunc(path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) {
  102. list, err := doScanDir(0, path, pattern, recursive, func(path string) string {
  103. if IsDir(path) {
  104. return ""
  105. }
  106. return handler(path)
  107. })
  108. if err != nil {
  109. return nil, err
  110. }
  111. if len(list) > 0 {
  112. sort.Strings(list)
  113. }
  114. return list, nil
  115. }
  116. // doScanDir is an internal method which scans directory and returns the absolute path
  117. // list of files that are not sorted.
  118. //
  119. // The pattern parameter `pattern` supports multiple file name patterns, using the ','
  120. // symbol to separate multiple patterns.
  121. //
  122. // The parameter `recursive` specifies whether scanning the `path` recursively, which
  123. // means it scans its sub-files and appends the files path to result array if the sub-file
  124. // is also a folder. It is false in default.
  125. //
  126. // The parameter `handler` specifies the callback function handling each sub-file path of
  127. // the `path` and its sub-folders. It ignores the sub-file path if `handler` returns an empty
  128. // string, or else it appends the sub-file path to result slice.
  129. func doScanDir(depth int, path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) {
  130. if depth >= maxScanDepth {
  131. return nil, gerror.Newf("directory scanning exceeds max recursive depth: %d", maxScanDepth)
  132. }
  133. var (
  134. list []string
  135. file, err = Open(path)
  136. )
  137. if err != nil {
  138. return nil, err
  139. }
  140. defer file.Close()
  141. names, err := file.Readdirnames(-1)
  142. if err != nil {
  143. err = gerror.Wrapf(err, `read directory files failed from path "%s"`, path)
  144. return nil, err
  145. }
  146. var (
  147. filePath string
  148. patterns = gstr.SplitAndTrim(pattern, ",")
  149. )
  150. for _, name := range names {
  151. filePath = path + Separator + name
  152. if IsDir(filePath) && recursive {
  153. array, _ := doScanDir(depth+1, filePath, pattern, true, handler)
  154. if len(array) > 0 {
  155. list = append(list, array...)
  156. }
  157. }
  158. // Handler filtering.
  159. if handler != nil {
  160. filePath = handler(filePath)
  161. if filePath == "" {
  162. continue
  163. }
  164. }
  165. // If it meets pattern, then add it to the result list.
  166. for _, p := range patterns {
  167. if match, _ := filepath.Match(p, name); match {
  168. if filePath = Abs(filePath); filePath != "" {
  169. list = append(list, filePath)
  170. }
  171. }
  172. }
  173. }
  174. return list, nil
  175. }