github.com/gogf/gf@v1.16.9/os/gfile/gfile.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 provides easy-to-use operations for file system.
     8  package gfile
     9  
    10  import (
    11  	"github.com/gogf/gf/text/gstr"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"strings"
    16  	"time"
    17  
    18  	"github.com/gogf/gf/container/gtype"
    19  	"github.com/gogf/gf/util/gconv"
    20  )
    21  
    22  var (
    23  	// Separator for file system.
    24  	// It here defines the separator as variable
    25  	// to allow it modified by developer if necessary.
    26  	Separator = string(filepath.Separator)
    27  
    28  	// DefaultPerm is the default perm for file opening.
    29  	DefaultPermOpen = os.FileMode(0666)
    30  
    31  	// DefaultPermCopy is the default perm for file/folder copy.
    32  	DefaultPermCopy = os.FileMode(0777)
    33  
    34  	// The absolute file path for main package.
    35  	// It can be only checked and set once.
    36  	mainPkgPath = gtype.NewString()
    37  
    38  	// selfPath is the current running binary path.
    39  	// As it is most commonly used, it is so defined as an internal package variable.
    40  	selfPath = ""
    41  
    42  	// Temporary directory of system.
    43  	tempDir = "/tmp"
    44  )
    45  
    46  func init() {
    47  	// Initialize internal package variable: tempDir.
    48  	if Separator != "/" || !Exists(tempDir) {
    49  		tempDir = os.TempDir()
    50  	}
    51  	// Initialize internal package variable: selfPath.
    52  	selfPath, _ = exec.LookPath(os.Args[0])
    53  	if selfPath != "" {
    54  		selfPath, _ = filepath.Abs(selfPath)
    55  	}
    56  	if selfPath == "" {
    57  		selfPath, _ = filepath.Abs(os.Args[0])
    58  	}
    59  }
    60  
    61  // Mkdir creates directories recursively with given <path>.
    62  // The parameter <path> is suggested to be an absolute path instead of relative one.
    63  func Mkdir(path string) error {
    64  	if err := os.MkdirAll(path, os.ModePerm); err != nil {
    65  		return err
    66  	}
    67  	return nil
    68  }
    69  
    70  // Create creates file with given <path> recursively.
    71  // The parameter <path> is suggested to be absolute path.
    72  func Create(path string) (*os.File, error) {
    73  	dir := Dir(path)
    74  	if !Exists(dir) {
    75  		if err := Mkdir(dir); err != nil {
    76  			return nil, err
    77  		}
    78  	}
    79  	return os.Create(path)
    80  }
    81  
    82  // Open opens file/directory READONLY.
    83  func Open(path string) (*os.File, error) {
    84  	return os.Open(path)
    85  }
    86  
    87  // OpenFile opens file/directory with custom <flag> and <perm>.
    88  // The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
    89  func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
    90  	return os.OpenFile(path, flag, perm)
    91  }
    92  
    93  // OpenWithFlag opens file/directory with default perm and custom <flag>.
    94  // The default <perm> is 0666.
    95  // The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
    96  func OpenWithFlag(path string, flag int) (*os.File, error) {
    97  	f, err := os.OpenFile(path, flag, DefaultPermOpen)
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  	return f, nil
   102  }
   103  
   104  // OpenWithFlagPerm opens file/directory with custom <flag> and <perm>.
   105  // The parameter <flag> is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc.
   106  // The parameter <perm> is like: 0600, 0666, 0777, etc.
   107  func OpenWithFlagPerm(path string, flag int, perm os.FileMode) (*os.File, error) {
   108  	f, err := os.OpenFile(path, flag, perm)
   109  	if err != nil {
   110  		return nil, err
   111  	}
   112  	return f, nil
   113  }
   114  
   115  // Join joins string array paths with file separator of current system.
   116  func Join(paths ...string) string {
   117  	var s string
   118  	for _, path := range paths {
   119  		if s != "" {
   120  			s += Separator
   121  		}
   122  		s += gstr.TrimRight(path, Separator)
   123  	}
   124  	return s
   125  }
   126  
   127  // Exists checks whether given <path> exist.
   128  func Exists(path string) bool {
   129  	if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) {
   130  		return true
   131  	}
   132  	return false
   133  }
   134  
   135  // IsDir checks whether given <path> a directory.
   136  // Note that it returns false if the <path> does not exist.
   137  func IsDir(path string) bool {
   138  	s, err := os.Stat(path)
   139  	if err != nil {
   140  		return false
   141  	}
   142  	return s.IsDir()
   143  }
   144  
   145  // Pwd returns absolute path of current working directory.
   146  // Note that it returns an empty string if retrieving current
   147  // working directory failed.
   148  func Pwd() string {
   149  	path, err := os.Getwd()
   150  	if err != nil {
   151  		return ""
   152  	}
   153  	return path
   154  }
   155  
   156  // Chdir changes the current working directory to the named directory.
   157  // If there is an error, it will be of type *PathError.
   158  func Chdir(dir string) error {
   159  	return os.Chdir(dir)
   160  }
   161  
   162  // IsFile checks whether given <path> a file, which means it's not a directory.
   163  // Note that it returns false if the <path> does not exist.
   164  func IsFile(path string) bool {
   165  	s, err := os.Stat(path)
   166  	if err != nil {
   167  		return false
   168  	}
   169  	return !s.IsDir()
   170  }
   171  
   172  // Alias of Stat.
   173  // See Stat.
   174  func Info(path string) (os.FileInfo, error) {
   175  	return Stat(path)
   176  }
   177  
   178  // Stat returns a FileInfo describing the named file.
   179  // If there is an error, it will be of type *PathError.
   180  func Stat(path string) (os.FileInfo, error) {
   181  	return os.Stat(path)
   182  }
   183  
   184  // Move renames (moves) <src> to <dst> path.
   185  // If <dst> already exists and is not a directory, it'll be replaced.
   186  func Move(src string, dst string) error {
   187  	return os.Rename(src, dst)
   188  }
   189  
   190  // Rename is alias of Move.
   191  // See Move.
   192  func Rename(src string, dst string) error {
   193  	return Move(src, dst)
   194  }
   195  
   196  // DirNames returns sub-file names of given directory <path>.
   197  // Note that the returned names are NOT absolute paths.
   198  func DirNames(path string) ([]string, error) {
   199  	f, err := os.Open(path)
   200  	if err != nil {
   201  		return nil, err
   202  	}
   203  	list, err := f.Readdirnames(-1)
   204  	f.Close()
   205  	if err != nil {
   206  		return nil, err
   207  	}
   208  	return list, nil
   209  }
   210  
   211  // Glob returns the names of all files matching pattern or nil
   212  // if there is no matching file. The syntax of patterns is the same
   213  // as in Match. The pattern may describe hierarchical names such as
   214  // /usr/*/bin/ed (assuming the Separator is '/').
   215  //
   216  // Glob ignores file system errors such as I/O errors reading directories.
   217  // The only possible returned error is ErrBadPattern, when pattern
   218  // is malformed.
   219  func Glob(pattern string, onlyNames ...bool) ([]string, error) {
   220  	if list, err := filepath.Glob(pattern); err == nil {
   221  		if len(onlyNames) > 0 && onlyNames[0] && len(list) > 0 {
   222  			array := make([]string, len(list))
   223  			for k, v := range list {
   224  				array[k] = Basename(v)
   225  			}
   226  			return array, nil
   227  		}
   228  		return list, nil
   229  	} else {
   230  		return nil, err
   231  	}
   232  }
   233  
   234  // Remove deletes all file/directory with <path> parameter.
   235  // If parameter <path> is directory, it deletes it recursively.
   236  func Remove(path string) error {
   237  	return os.RemoveAll(path)
   238  }
   239  
   240  // IsReadable checks whether given <path> is readable.
   241  func IsReadable(path string) bool {
   242  	result := true
   243  	file, err := os.OpenFile(path, os.O_RDONLY, DefaultPermOpen)
   244  	if err != nil {
   245  		result = false
   246  	}
   247  	file.Close()
   248  	return result
   249  }
   250  
   251  // IsWritable checks whether given <path> is writable.
   252  //
   253  // TODO improve performance; use golang.org/x/sys to cross-plat-form
   254  func IsWritable(path string) bool {
   255  	result := true
   256  	if IsDir(path) {
   257  		// If it's a directory, create a temporary file to test whether it's writable.
   258  		tmpFile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano())
   259  		if f, err := Create(tmpFile); err != nil || !Exists(tmpFile) {
   260  			result = false
   261  		} else {
   262  			f.Close()
   263  			Remove(tmpFile)
   264  		}
   265  	} else {
   266  		// 如果是文件,那么判断文件是否可打开
   267  		file, err := os.OpenFile(path, os.O_WRONLY, DefaultPermOpen)
   268  		if err != nil {
   269  			result = false
   270  		}
   271  		file.Close()
   272  	}
   273  	return result
   274  }
   275  
   276  // See os.Chmod.
   277  func Chmod(path string, mode os.FileMode) error {
   278  	return os.Chmod(path, mode)
   279  }
   280  
   281  // Abs returns an absolute representation of path.
   282  // If the path is not absolute it will be joined with the current
   283  // working directory to turn it into an absolute path. The absolute
   284  // path name for a given file is not guaranteed to be unique.
   285  // Abs calls Clean on the result.
   286  func Abs(path string) string {
   287  	p, _ := filepath.Abs(path)
   288  	return p
   289  }
   290  
   291  // RealPath converts the given <path> to its absolute path
   292  // and checks if the file path exists.
   293  // If the file does not exist, return an empty string.
   294  func RealPath(path string) string {
   295  	p, err := filepath.Abs(path)
   296  	if err != nil {
   297  		return ""
   298  	}
   299  	if !Exists(p) {
   300  		return ""
   301  	}
   302  	return p
   303  }
   304  
   305  // SelfPath returns absolute file path of current running process(binary).
   306  func SelfPath() string {
   307  	return selfPath
   308  }
   309  
   310  // SelfName returns file name of current running process(binary).
   311  func SelfName() string {
   312  	return Basename(SelfPath())
   313  }
   314  
   315  // SelfDir returns absolute directory path of current running process(binary).
   316  func SelfDir() string {
   317  	return filepath.Dir(SelfPath())
   318  }
   319  
   320  // Basename returns the last element of path, which contains file extension.
   321  // Trailing path separators are removed before extracting the last element.
   322  // If the path is empty, Base returns ".".
   323  // If the path consists entirely of separators, Basename returns a single separator.
   324  // Example:
   325  // /var/www/file.js -> file.js
   326  // file.js          -> file.js
   327  func Basename(path string) string {
   328  	return filepath.Base(path)
   329  }
   330  
   331  // Name returns the last element of path without file extension.
   332  // Example:
   333  // /var/www/file.js -> file
   334  // file.js          -> file
   335  func Name(path string) string {
   336  	base := filepath.Base(path)
   337  	if i := strings.LastIndexByte(base, '.'); i != -1 {
   338  		return base[:i]
   339  	}
   340  	return base
   341  }
   342  
   343  // Dir returns all but the last element of path, typically the path's directory.
   344  // After dropping the final element, Dir calls Clean on the path and trailing
   345  // slashes are removed.
   346  // If the `path` is empty, Dir returns ".".
   347  // If the `path` is ".", Dir treats the path as current working directory.
   348  // If the `path` consists entirely of separators, Dir returns a single separator.
   349  // The returned path does not end in a separator unless it is the root directory.
   350  func Dir(path string) string {
   351  	if path == "." {
   352  		return filepath.Dir(RealPath(path))
   353  	}
   354  	return filepath.Dir(path)
   355  }
   356  
   357  // IsEmpty checks whether the given <path> is empty.
   358  // If <path> is a folder, it checks if there's any file under it.
   359  // If <path> is a file, it checks if the file size is zero.
   360  //
   361  // Note that it returns true if <path> does not exist.
   362  func IsEmpty(path string) bool {
   363  	stat, err := Stat(path)
   364  	if err != nil {
   365  		return true
   366  	}
   367  	if stat.IsDir() {
   368  		file, err := os.Open(path)
   369  		if err != nil {
   370  			return true
   371  		}
   372  		defer file.Close()
   373  		names, err := file.Readdirnames(-1)
   374  		if err != nil {
   375  			return true
   376  		}
   377  		return len(names) == 0
   378  	} else {
   379  		return stat.Size() == 0
   380  	}
   381  }
   382  
   383  // Ext returns the file name extension used by path.
   384  // The extension is the suffix beginning at the final dot
   385  // in the final element of path; it is empty if there is
   386  // no dot.
   387  //
   388  // Note: the result contains symbol '.'.
   389  func Ext(path string) string {
   390  	ext := filepath.Ext(path)
   391  	if p := strings.IndexByte(ext, '?'); p != -1 {
   392  		ext = ext[0:p]
   393  	}
   394  	return ext
   395  }
   396  
   397  // ExtName is like function Ext, which returns the file name extension used by path,
   398  // but the result does not contains symbol '.'.
   399  func ExtName(path string) string {
   400  	return strings.TrimLeft(Ext(path), ".")
   401  }
   402  
   403  // TempDir retrieves and returns the temporary directory of current system.
   404  // It return "/tmp" is current in *nix system, or else it returns os.TempDir().
   405  //
   406  // The optional parameter <names> specifies the its sub-folders/sub-files,
   407  // which will be joined with current system separator and returned with the path.
   408  func TempDir(names ...string) string {
   409  	path := tempDir
   410  	for _, name := range names {
   411  		path += Separator + name
   412  	}
   413  	return path
   414  }