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