gf/os/gfile/gfile.go

492 lines
12 KiB
Go
Raw Normal View History

// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
2017-12-29 16:03:30 +08:00
//
// 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.
2017-12-29 16:03:30 +08:00
2019-01-15 23:27:47 +08:00
// Package gfile provides easy-to-use operations for file system.
2017-11-23 10:21:28 +08:00
package gfile
import (
2019-04-22 22:33:11 +08:00
"bytes"
"errors"
"fmt"
"io"
2019-07-08 01:26:00 +08:00
"io/ioutil"
2019-04-22 22:33:11 +08:00
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"strings"
"time"
2019-07-08 11:37:02 +08:00
2019-07-29 21:01:19 +08:00
"github.com/gogf/gf/container/gtype"
"github.com/gogf/gf/util/gconv"
2017-11-23 10:21:28 +08:00
)
2018-04-19 14:58:25 +08:00
const (
Separator = string(filepath.Separator) // Separator for file system.
gDEFAULT_PERM = 0666 // Default perm for file opening.
2018-04-19 14:58:25 +08:00
)
2017-11-23 10:21:28 +08:00
2018-08-21 21:18:56 +08:00
var (
2019-06-19 09:06:52 +08:00
// The absolute file path for main package.
// It can be only checked and set once.
mainPkgPath = gtype.NewString()
2018-08-21 21:18:56 +08:00
)
2019-05-23 21:58:04 +08:00
// Mkdir creates directories recursively with given <path>.
// The parameter <path> is suggested to be absolute path.
2017-11-23 10:21:28 +08:00
func Mkdir(path string) error {
2019-06-19 09:06:52 +08:00
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
return nil
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// Create creates file with given <path> recursively.
// The parameter <path> is suggested to be absolute path.
2019-02-15 21:30:35 +08:00
func Create(path string) (*os.File, error) {
2019-06-19 09:06:52 +08:00
dir := Dir(path)
if !Exists(dir) {
Mkdir(dir)
}
return os.Create(path)
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// Open opens file/directory readonly.
2017-11-23 10:21:28 +08:00
func Open(path string) (*os.File, error) {
2019-06-19 09:06:52 +08:00
return os.Open(path)
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// OpenFile opens file/directory with given <flag> and <perm>.
2019-02-15 21:30:35 +08:00
func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
2019-06-19 09:06:52 +08:00
return os.OpenFile(path, flag, perm)
2019-02-15 21:30:35 +08:00
}
2019-05-23 21:58:04 +08:00
// OpenWithFlag opens file/directory with default perm and given <flag>.
2017-11-23 10:21:28 +08:00
func OpenWithFlag(path string, flag int) (*os.File, error) {
2019-06-19 09:06:52 +08:00
f, err := os.OpenFile(path, flag, gDEFAULT_PERM)
if err != nil {
return nil, err
}
return f, nil
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// OpenWithFlagPerm opens file/directory with given <flag> and <perm>.
2018-10-22 18:58:11 +08:00
func OpenWithFlagPerm(path string, flag int, perm int) (*os.File, error) {
2019-06-19 09:06:52 +08:00
f, err := os.OpenFile(path, flag, os.FileMode(perm))
if err != nil {
return nil, err
}
return f, nil
2018-10-22 18:58:11 +08:00
}
2019-05-23 21:58:04 +08:00
// Exists checks whether given <path> exist.
2017-11-23 10:21:28 +08:00
func Exists(path string) bool {
2019-06-19 09:06:52 +08:00
if _, err := os.Stat(path); !os.IsNotExist(err) {
return true
}
return false
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// IsDir checks whether given <path> a directory.
2017-11-23 10:21:28 +08:00
func IsDir(path string) bool {
2019-06-19 09:06:52 +08:00
s, err := os.Stat(path)
if err != nil {
return false
}
return s.IsDir()
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// Pwd returns absolute path of current working directory.
2018-08-14 19:53:31 +08:00
func Pwd() string {
2019-06-19 09:06:52 +08:00
path, _ := os.Getwd()
return path
2018-08-14 19:53:31 +08:00
}
2019-05-23 21:58:04 +08:00
// IsFile checks whether given <path> a file, which means it's not a directory.
2017-11-23 10:21:28 +08:00
func IsFile(path string) bool {
2019-06-19 09:06:52 +08:00
s, err := os.Stat(path)
if err != nil {
return false
}
return !s.IsDir()
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// Alias of Stat.
// See Stat.
func Info(path string) (os.FileInfo, error) {
2019-06-19 09:06:52 +08:00
return Stat(path)
}
// Stat returns a FileInfo describing the named file.
2019-02-15 21:30:35 +08:00
// If there is an error, it will be of type *PathError.
func Stat(path string) (os.FileInfo, error) {
2019-06-19 09:06:52 +08:00
return os.Stat(path)
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// Move renames (moves) <src> to <dst> path.
2017-11-23 10:21:28 +08:00
func Move(src string, dst string) error {
2019-06-19 09:06:52 +08:00
return os.Rename(src, dst)
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// Alias of Move.
// See Move.
2017-11-23 10:21:28 +08:00
func Rename(src string, dst string) error {
2019-06-19 09:06:52 +08:00
return Move(src, dst)
2017-11-23 10:21:28 +08:00
}
2019-07-09 11:02:33 +08:00
// Copy file/directory from <src> to <dst>.
2019-02-15 21:30:35 +08:00
//
2019-07-09 11:02:33 +08:00
// If <src> is file, it calls CopyFile to implements copy feature,
// or else it calls CopyDir.
2017-11-23 10:21:28 +08:00
func Copy(src string, dst string) error {
2019-07-09 11:02:33 +08:00
if IsFile(src) {
return CopyFile(src, dst)
}
return CopyDir(src, dst)
}
// CopyFile copies the contents of the file named src to the file named
// by dst. The file will be created if it does not already exist. If the
// destination file exists, all it's contents will be replaced by the contents
// of the source file. The file mode will be copied from the source and
// the copied data is synced/flushed to stable storage.
// Thanks: https://gist.github.com/r0l1/92462b38df26839a3ca324697c8cba04
func CopyFile(src, dst string) (err error) {
in, err := os.Open(src)
2019-06-19 09:06:52 +08:00
if err != nil {
2019-07-09 11:02:33 +08:00
return
2019-06-19 09:06:52 +08:00
}
2019-07-09 11:02:33 +08:00
defer func() {
if e := in.Close(); e != nil {
err = e
}
}()
out, err := os.Create(dst)
2019-06-19 09:06:52 +08:00
if err != nil {
2019-07-09 11:02:33 +08:00
return
}
defer func() {
if e := out.Close(); e != nil {
err = e
}
}()
_, err = io.Copy(out, in)
if err != nil {
return
2019-06-19 09:06:52 +08:00
}
2019-07-09 11:02:33 +08:00
err = out.Sync()
2019-06-19 09:06:52 +08:00
if err != nil {
2019-07-09 11:02:33 +08:00
return
}
si, err := os.Stat(src)
if err != nil {
return
2019-06-19 09:06:52 +08:00
}
2019-07-09 11:02:33 +08:00
err = os.Chmod(dst, si.Mode())
if err != nil {
return
}
return
}
// CopyDir recursively copies a directory tree, attempting to preserve permissions.
// Source directory must exist, destination directory must *not* exist.
// Symlinks are ignored and skipped.
func CopyDir(src string, dst string) (err error) {
src = filepath.Clean(src)
dst = filepath.Clean(dst)
si, err := os.Stat(src)
2019-06-19 09:06:52 +08:00
if err != nil {
return err
}
2019-07-09 11:02:33 +08:00
if !si.IsDir() {
return fmt.Errorf("source is not a directory")
}
_, err = os.Stat(dst)
if err != nil && !os.IsNotExist(err) {
return
}
if err == nil {
return fmt.Errorf("destination already exists")
}
err = os.MkdirAll(dst, si.Mode())
if err != nil {
return
}
entries, err := ioutil.ReadDir(src)
if err != nil {
return
}
for _, entry := range entries {
srcPath := filepath.Join(src, entry.Name())
dstPath := filepath.Join(dst, entry.Name())
if entry.IsDir() {
err = CopyDir(srcPath, dstPath)
if err != nil {
return
}
} else {
// Skip symlinks.
if entry.Mode()&os.ModeSymlink != 0 {
continue
}
err = CopyFile(srcPath, dstPath)
if err != nil {
return
}
}
}
return
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// DirNames returns sub-file names of given directory <path>.
func DirNames(path string) ([]string, error) {
2019-06-19 09:06:52 +08:00
f, err := os.Open(path)
if err != nil {
return nil, err
}
list, err := f.Readdirnames(-1)
f.Close()
if err != nil {
return nil, err
}
return list, nil
}
2019-02-15 21:30:35 +08:00
// Glob returns the names of all files matching pattern or nil
// if there is no matching file. The syntax of patterns is the same
// as in Match. The pattern may describe hierarchical names such as
// /usr/*/bin/ed (assuming the Separator is '/').
//
// Glob ignores file system errors such as I/O errors reading directories.
// The only possible returned error is ErrBadPattern, when pattern
// is malformed.
2019-06-19 09:06:52 +08:00
func Glob(pattern string, onlyNames ...bool) ([]string, error) {
if list, err := filepath.Glob(pattern); err == nil {
if len(onlyNames) > 0 && onlyNames[0] && len(list) > 0 {
array := make([]string, len(list))
for k, v := range list {
array[k] = Basename(v)
}
return array, nil
}
return list, nil
} else {
return nil, err
}
}
2019-05-23 21:58:04 +08:00
// Remove deletes all file/directory with <path> parameter.
// If parameter <path> is directory, it deletes it recursively.
2017-11-23 10:21:28 +08:00
func Remove(path string) error {
2019-06-19 09:06:52 +08:00
return os.RemoveAll(path)
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// IsReadable checks whether given <path> is readable.
2017-11-23 10:21:28 +08:00
func IsReadable(path string) bool {
2019-06-19 09:06:52 +08:00
result := true
file, err := os.OpenFile(path, os.O_RDONLY, gDEFAULT_PERM)
if err != nil {
result = false
}
file.Close()
return result
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// IsWritable checks whether given <path> is writable.
2019-02-15 21:30:35 +08:00
//
2019-05-23 21:58:04 +08:00
// @TODO improve performance; use golang.org/x/sys to cross-plat-form
2017-11-23 10:21:28 +08:00
func IsWritable(path string) bool {
2019-06-19 09:06:52 +08:00
result := true
if IsDir(path) {
// If it's a directory, create a temporary file to test whether it's writable.
tmpFile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano())
if f, err := Create(tmpFile); err != nil || !Exists(tmpFile) {
result = false
} else {
f.Close()
Remove(tmpFile)
}
} else {
// 如果是文件,那么判断文件是否可打开
file, err := os.OpenFile(path, os.O_WRONLY, gDEFAULT_PERM)
if err != nil {
result = false
}
file.Close()
}
return result
2017-11-23 10:21:28 +08:00
}
2019-02-15 21:30:35 +08:00
// See os.Chmod.
2017-11-23 10:21:28 +08:00
func Chmod(path string, mode os.FileMode) error {
2019-06-19 09:06:52 +08:00
return os.Chmod(path, mode)
2017-11-23 10:21:28 +08:00
}
2019-07-26 23:01:12 +08:00
// Abs returns an absolute representation of path.
// If the path is not absolute it will be joined with the current
// working directory to turn it into an absolute path. The absolute
// path name for a given file is not guaranteed to be unique.
// Abs calls Clean on the result.
func Abs(path string) string {
p, _ := filepath.Abs(path)
return p
}
2019-05-23 21:58:04 +08:00
// RealPath converts the given <path> to its absolute path
// and checks if the file path exists.
// If the file does not exist, return an empty string.
2017-11-23 10:21:28 +08:00
func RealPath(path string) string {
2019-06-19 09:06:52 +08:00
p, err := filepath.Abs(path)
if err != nil {
return ""
}
if !Exists(p) {
return ""
}
return p
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// SelfPath returns absolute file path of current running process(binary).
2017-11-23 10:21:28 +08:00
func SelfPath() string {
path, _ := exec.LookPath(os.Args[0])
if path != "" {
path, _ = filepath.Abs(path)
if path != "" {
return path
}
}
if path == "" {
path, _ = filepath.Abs(os.Args[0])
}
return path
2017-11-23 10:21:28 +08:00
}
// SelfName returns file name of current running process(binary).
func SelfName() string {
return Basename(SelfPath())
}
2019-05-23 21:58:04 +08:00
// SelfDir returns absolute directory path of current running process(binary).
2017-11-23 10:21:28 +08:00
func SelfDir() string {
2019-06-19 09:06:52 +08:00
return filepath.Dir(SelfPath())
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// Basename returns the last element of path.
// Trailing path separators are removed before extracting the last element.
// If the path is empty, Base returns ".".
// If the path consists entirely of separators, Basename returns a single separator.
2017-11-23 10:21:28 +08:00
func Basename(path string) string {
2019-06-19 09:06:52 +08:00
return filepath.Base(path)
2017-11-23 10:21:28 +08:00
}
// Name returns the last element of path without extension.
func Name(path string) string {
base := filepath.Base(path)
if i := strings.LastIndexByte(base, '.'); i != -1 {
return base[:i]
}
return base
}
2019-05-23 21:58:04 +08:00
// Dir returns all but the last element of path, typically the path's directory.
// After dropping the final element, Dir calls Clean on the path and trailing
// slashes are removed.
// If the path is empty, Dir returns ".".
// If the path consists entirely of separators, Dir returns a single separator.
// The returned path does not end in a separator unless it is the root directory.
2017-11-23 10:21:28 +08:00
func Dir(path string) string {
2019-06-19 09:06:52 +08:00
return filepath.Dir(path)
2017-11-23 10:21:28 +08:00
}
2019-09-05 19:22:11 +08:00
// IsEmpty checks whether the given <path> is empty.
// If <path> is a folder, it checks if there's any file under it.
// If <path> is a file, it checks if the file size is zero.
//
// Note that it returns true if <path> does not exist.
2019-09-05 19:22:11 +08:00
func IsEmpty(path string) bool {
stat, err := Stat(path)
if err != nil {
return true
2019-09-05 19:22:11 +08:00
}
if stat.IsDir() {
file, err := os.Open(path)
if err != nil {
return true
2019-09-05 19:22:11 +08:00
}
defer file.Close()
names, err := file.Readdirnames(-1)
if err != nil {
return true
2019-09-05 19:22:11 +08:00
}
return len(names) == 0
} else {
return stat.Size() == 0
}
}
2019-05-23 21:58:04 +08:00
// Ext returns the file name extension used by path.
// The extension is the suffix beginning at the final dot
// in the final element of path; it is empty if there is
// no dot.
2019-02-15 21:30:35 +08:00
//
2019-05-23 21:58:04 +08:00
// Note: the result contains symbol '.'.
2017-11-23 10:21:28 +08:00
func Ext(path string) string {
2019-06-19 09:06:52 +08:00
return filepath.Ext(path)
2017-11-23 10:21:28 +08:00
}
2019-05-23 21:58:04 +08:00
// Home returns absolute path of current user's home directory.
2017-11-23 10:21:28 +08:00
func Home() (string, error) {
2019-06-19 09:06:52 +08:00
u, err := user.Current()
if nil == err {
return u.HomeDir, nil
}
if "windows" == runtime.GOOS {
return homeWindows()
}
return homeUnix()
2017-11-23 10:21:28 +08:00
}
func homeUnix() (string, error) {
2019-06-19 09:06:52 +08:00
if home := os.Getenv("HOME"); home != "" {
return home, nil
}
var stdout bytes.Buffer
cmd := exec.Command("sh", "-c", "eval echo ~$USER")
cmd.Stdout = &stdout
if err := cmd.Run(); err != nil {
return "", err
}
2017-11-23 10:21:28 +08:00
2019-06-19 09:06:52 +08:00
result := strings.TrimSpace(stdout.String())
if result == "" {
return "", errors.New("blank output when reading home directory")
}
2017-11-23 10:21:28 +08:00
2019-06-19 09:06:52 +08:00
return result, nil
2017-11-23 10:21:28 +08:00
}
func homeWindows() (string, error) {
2019-06-19 09:06:52 +08:00
drive := os.Getenv("HOMEDRIVE")
path := os.Getenv("HOMEPATH")
home := drive + path
if drive == "" || path == "" {
home = os.Getenv("USERPROFILE")
}
if home == "" {
return "", errors.New("HOMEDRIVE, HOMEPATH, and USERPROFILE are blank")
}
2017-11-23 10:21:28 +08:00
2019-06-19 09:06:52 +08:00
return home, nil
2017-11-23 10:21:28 +08:00
}
2019-02-15 21:30:35 +08:00
// See os.TempDir().
func TempDir() string {
2019-06-19 09:06:52 +08:00
return os.TempDir()
}