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  }