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