gf/g/os/gfile/gfile.go

495 lines
12 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Copyright 2017 gf Author(https://github.com/gogf/gf). 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 provides easy-to-use operations for file system.
//
// 文件管理.
package gfile
import (
"bytes"
"errors"
"fmt"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
"io"
"os"
"os/exec"
"os/user"
"path/filepath"
"runtime"
"sort"
"strings"
"time"
)
const (
// 文件分隔符
Separator = string(filepath.Separator)
// 默认的文件打开权限
gDEFAULT_PERM = 0666
)
var (
// 源码的main包所在目录仅仅会设置一次
mainPkgPath = gtype.NewString()
// 编译时的 GOROOT 数值
goRootOfBuild = gtype.NewString()
)
// Create directories recursively.
//
// 给定目录的绝对路径创建目录(递归创建)。
func Mkdir(path string) error {
err := os.MkdirAll(path, os.ModePerm)
if err != nil {
return err
}
return nil
}
// Create file with given path recursively.
//
// 给定文件的绝对路径创建文件。
func Create(path string) (*os.File, error) {
dir := Dir(path)
if !Exists(dir) {
Mkdir(dir)
}
return os.Create(path)
}
// Open file/directory with readonly.
//
// 只读打开文件
func Open(path string) (*os.File, error) {
return os.Open(path)
}
// Open file/directory with given <flag> and <perm>.
//
// 打开文件(带flag&perm)
func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) {
return os.OpenFile(path, flag, perm)
}
// Open file/directory with default perm and given <flag>.
//
// 打开文件(带flag)
func OpenWithFlag(path string, flag int) (*os.File, error) {
f, err := os.OpenFile(path, flag, gDEFAULT_PERM)
if err != nil {
return nil, err
}
return f, nil
}
// Open file/directory with given <flag> and <perm>.
//
// 打开文件(带flag&perm)
func OpenWithFlagPerm(path string, flag int, perm int) (*os.File, error) {
f, err := os.OpenFile(path, flag, os.FileMode(perm))
if err != nil {
return nil, err
}
return f, nil
}
// Check whether given path exist.
//
// 判断所给路径文件/文件夹是否存在
func Exists(path string) bool {
if _, err := os.Stat(path); !os.IsNotExist(err) {
return true
}
return false
}
// Check whether given path a directory.
//
// 判断所给路径是否为文件夹
func IsDir(path string) bool {
s, err := os.Stat(path)
if err != nil {
return false
}
return s.IsDir()
}
// Get current working directory absolute path.
//
// 获取当前工作目录(注意与SelfDir的区别).
func Pwd() string {
path, _ := os.Getwd()
return path
}
// Check whether given path a file(not a directory).
//
// 判断所给路径是否为文件
func IsFile(path string) bool {
s, err := os.Stat(path)
if err != nil {
return false
}
return !s.IsDir()
}
// See Stat.
//
// Stat 方法的别名。
func Info(path string) (os.FileInfo, error) {
return Stat(path)
}
// Stat returns a FileInfo describing the named file.
// If there is an error, it will be of type *PathError.
//
// 获取文件或目录信息.
func Stat(path string) (os.FileInfo, error) {
return os.Stat(path)
}
// Move renames (moves) src to dst path.
//
// 文件移动/重命名
func Move(src string, dst string) error {
return os.Rename(src, dst)
}
// Rename renames (moves) src to dst path.
//
// 文件移动/重命名.
func Rename(src string, dst string) error {
return Move(src, dst)
}
// Copy file from src to dst.
//
// 文件复制.
// @TODO 支持目录复制.
func Copy(src string, dst string) error {
srcFile, err := Open(src)
if err != nil {
return err
}
defer srcFile.Close()
dstFile, err := Create(dst)
if err != nil {
return err
}
defer dstFile.Close()
_, err = io.Copy(dstFile, srcFile)
if err != nil {
return err
}
err = dstFile.Sync()
if err != nil {
return err
}
return nil
}
// Get sub-file names of path.
//
// 返回目录下的文件名列表
func DirNames(path string) ([]string, error) {
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
}
// 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.
//
// 文件名正则匹配查找,第二个可选参数指定返回的列表是否仅为文件名(非绝对路径),默认返回绝对路径
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
}
}
// Remove file/directory with <path> parameter.
//
// 文件/目录删除
func Remove(path string) error {
return os.RemoveAll(path)
}
// Check whether given <path> is readable.
//
// 文件是否可读(支持文件/目录)
func IsReadable(path string) bool {
result := true
file, err := os.OpenFile(path, os.O_RDONLY, gDEFAULT_PERM)
if err != nil {
result = false
}
file.Close()
return result
}
// Check whether given <path> is writable.
//
// 文件是否可写(支持文件/目录)
// @TODO 改进性能,利用 golang.org/x/sys 来实现跨平台的权限判断。
func IsWritable(path string) bool {
result := true
if IsDir(path) {
// 如果是目录,那么创建一个临时文件进行写入测试
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
}
// See os.Chmod.
//
// 修改文件/目录权限
func Chmod(path string, mode os.FileMode) error {
return os.Chmod(path, mode)
}
// Get all sub-files(absolute) of given <path>,
// can be recursively with given parameter <recursive> true.
//
// 打开目录,并返回其下一级文件列表(绝对路径),按照文件名称大小写进行排序,支持目录递归遍历。
func ScanDir(path string, pattern string, recursive ... bool) ([]string, error) {
list, err := doScanDir(path, pattern, recursive...)
if err != nil {
return nil, err
}
if len(list) > 0 {
sort.Strings(list)
}
return list, nil
}
// 内部检索目录方法,支持递归,返回没有排序的文件绝对路径列表结果。
// pattern参数支持多个文件名称模式匹配使用','符号分隔多个模式。
func doScanDir(path string, pattern string, recursive ... bool) ([]string, error) {
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
}
// 是否递归遍历
for _, name := range names {
path := fmt.Sprintf("%s%s%s", path, Separator, name)
if IsDir(path) && len(recursive) > 0 && recursive[0] {
array, _ := doScanDir(path, pattern, true)
if len(array) > 0 {
list = append(list, array...)
}
}
// 满足pattern才加入结果列表
for _, p := range strings.Split(pattern, ",") {
if match, err := filepath.Match(strings.TrimSpace(p), name); err == nil && match {
list = append(list, path)
}
}
}
return list, nil
}
// See filepath.Abs.
//
// 将所给定的路径转换为绝对路径
// 并判断文件路径是否存在,如果文件不存在,那么返回空字符串
func RealPath(path string) string {
p, err := filepath.Abs(path)
if err != nil {
return ""
}
if !Exists(p) {
return ""
}
return p
}
// Get absolute file path of current running process(binary).
//
// 获取当前执行文件的绝对路径
func SelfPath() string {
p, _ := filepath.Abs(os.Args[0])
return p
}
// Get absolute directory path of current running process(binary).
//
// 获取当前执行文件的目录绝对路径
func SelfDir() string {
return filepath.Dir(SelfPath())
}
// See filepath.Base.
//
// 获取指定文件路径的文件名称
func Basename(path string) string {
return filepath.Base(path)
}
// See filepath.Dir.
//
// 获取指定文件路径的目录地址绝对路径.
func Dir(path string) string {
return filepath.Dir(path)
}
// See filepath.Ext.
//
// 获取指定文件路径的文件扩展名(包含"."号)
func Ext(path string) string {
return filepath.Ext(path)
}
// Get absolute home directory path of current user.
//
// 获取用户主目录
func Home() (string, error) {
u, err := user.Current()
if nil == err {
return u.HomeDir, nil
}
if "windows" == runtime.GOOS {
return homeWindows()
}
return homeUnix()
}
func homeUnix() (string, error) {
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
}
result := strings.TrimSpace(stdout.String())
if result == "" {
return "", errors.New("blank output when reading home directory")
}
return result, nil
}
func homeWindows() (string, error) {
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")
}
return home, nil
}
// Get absolute file path of main file, which contains the entrance function main.
// Available in develop environment.
//
// 获取入口函数文件所在目录(main包文件目录),
// **仅对源码开发环境有效(即仅对生成该可执行文件的系统下有效)**
func MainPkgPath() string {
path := mainPkgPath.Val()
if path != "" {
return path
}
f := ""
goroot := runtime.GOROOT()
// runtime.GOROOT() 在windows下有可能是以'\'符号分隔,
// 而 runtime.Caller(i) 获取到的文件路径却是以'/'符号分隔,
// 因此这里统一转换为'/'符号再进行比较
goroot = gstr.Replace(goroot, "\\", "/")
for i := 1; i < 10000; i++ {
if _, file, _, ok := runtime.Caller(i); ok {
// 不包含go源码路径
if file != "" && goroot != "" &&
!gregex.IsMatchString("^" + goroot, file) &&
!strings.EqualFold("<autogenerated>", file) {
f = file
}
} else {
break
}
}
if f != "" {
for {
p := Dir(f)
if p == f {
break
}
// 会自动扫描源码寻找main包
if paths, err := ScanDir(p, "*.go"); err == nil && len(paths) > 0 {
for _, path := range paths {
if gregex.IsMatchString(`package\s+main`, GetContents(path)) {
mainPkgPath.Set(p)
return p
}
}
}
f = p
}
}
return ""
}
// See os.TempDir().
//
// 系统临时目录
func TempDir() string {
return os.TempDir()
}