gf/os/gfile/gfile_scan.go
2021-11-15 20:26:31 +08:00

185 lines
5.9 KiB
Go

// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package gfile
import (
"os"
"path/filepath"
"sort"
"github.com/gogf/gf/v2/errors/gcode"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/text/gstr"
)
const (
// Max recursive depth for directory scanning.
maxScanDepth = 100000
)
// ScanDir returns all sub-files with absolute paths of given `path`,
// It scans directory recursively if given parameter `recursive` is true.
//
// The pattern parameter `pattern` supports multiple file name patterns,
// using the ',' symbol to separate multiple patterns.
func ScanDir(path string, pattern string, recursive ...bool) ([]string, error) {
isRecursive := false
if len(recursive) > 0 {
isRecursive = recursive[0]
}
list, err := doScanDir(0, path, pattern, isRecursive, nil)
if err != nil {
return nil, err
}
if len(list) > 0 {
sort.Strings(list)
}
return list, nil
}
// ScanDirFunc returns all sub-files with absolute paths of given `path`,
// It scans directory recursively if given parameter `recursive` is true.
//
// The pattern parameter `pattern` supports multiple file name patterns, using the ','
// symbol to separate multiple patterns.
//
// The parameter `recursive` specifies whether scanning the `path` recursively, which
// means it scans its sub-files and appends the files path to result array if the sub-file
// is also a folder. It is false in default.
//
// The parameter `handler` specifies the callback function handling each sub-file path of
// the `path` and its sub-folders. It ignores the sub-file path if `handler` returns an empty
// string, or else it appends the sub-file path to result slice.
func ScanDirFunc(path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) {
list, err := doScanDir(0, path, pattern, recursive, handler)
if err != nil {
return nil, err
}
if len(list) > 0 {
sort.Strings(list)
}
return list, nil
}
// ScanDirFile returns all sub-files with absolute paths of given `path`,
// It scans directory recursively if given parameter `recursive` is true.
//
// The pattern parameter `pattern` supports multiple file name patterns,
// using the ',' symbol to separate multiple patterns.
//
// Note that it returns only files, exclusive of directories.
func ScanDirFile(path string, pattern string, recursive ...bool) ([]string, error) {
isRecursive := false
if len(recursive) > 0 {
isRecursive = recursive[0]
}
list, err := doScanDir(0, path, pattern, isRecursive, func(path string) string {
if IsDir(path) {
return ""
}
return path
})
if err != nil {
return nil, err
}
if len(list) > 0 {
sort.Strings(list)
}
return list, nil
}
// ScanDirFileFunc returns all sub-files with absolute paths of given `path`,
// It scans directory recursively if given parameter `recursive` is true.
//
// The pattern parameter `pattern` supports multiple file name patterns, using the ','
// symbol to separate multiple patterns.
//
// The parameter `recursive` specifies whether scanning the `path` recursively, which
// means it scans its sub-files and appends the files path to result array if the sub-file
// is also a folder. It is false in default.
//
// The parameter `handler` specifies the callback function handling each sub-file path of
// the `path` and its sub-folders. It ignores the sub-file path if `handler` returns an empty
// string, or else it appends the sub-file path to result slice.
//
// Note that the parameter `path` for `handler` is not a directory but a file.
// It returns only files, exclusive of directories.
func ScanDirFileFunc(path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) {
list, err := doScanDir(0, path, pattern, recursive, func(path string) string {
if IsDir(path) {
return ""
}
return handler(path)
})
if err != nil {
return nil, err
}
if len(list) > 0 {
sort.Strings(list)
}
return list, nil
}
// doScanDir is an internal method which scans directory and returns the absolute path
// list of files that are not sorted.
//
// The pattern parameter `pattern` supports multiple file name patterns, using the ','
// symbol to separate multiple patterns.
//
// The parameter `recursive` specifies whether scanning the `path` recursively, which
// means it scans its sub-files and appends the files path to result array if the sub-file
// is also a folder. It is false in default.
//
// The parameter `handler` specifies the callback function handling each sub-file path of
// the `path` and its sub-folders. It ignores the sub-file path if `handler` returns an empty
// string, or else it appends the sub-file path to result slice.
func doScanDir(depth int, path string, pattern string, recursive bool, handler func(path string) string) ([]string, error) {
if depth >= maxScanDepth {
return nil, gerror.NewCodef(gcode.CodeOperationFailed, "directory scanning exceeds max recursive depth: %d", maxScanDepth)
}
list := ([]string)(nil)
file, err := os.Open(path)
if err != nil {
return nil, err
}
defer file.Close()
names, err := file.Readdirnames(-1)
if err != nil {
return nil, err
}
var (
filePath = ""
patterns = gstr.SplitAndTrim(pattern, ",")
)
for _, name := range names {
filePath = path + Separator + name
if IsDir(filePath) && recursive {
array, _ := doScanDir(depth+1, filePath, pattern, true, handler)
if len(array) > 0 {
list = append(list, array...)
}
}
// Handler filtering.
if handler != nil {
filePath = handler(filePath)
if filePath == "" {
continue
}
}
// If it meets pattern, then add it to the result list.
for _, p := range patterns {
if match, err := filepath.Match(p, name); err == nil && match {
filePath = Abs(filePath)
if filePath != "" {
list = append(list, filePath)
}
}
}
}
return list, nil
}