mirror of
https://gitee.com/johng/gf.git
synced 2024-12-01 11:48:09 +08:00
422 lines
10 KiB
Go
422 lines
10 KiB
Go
// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
|
||
|
||
// 文件管理.
|
||
package gfile
|
||
|
||
import (
|
||
"bytes"
|
||
"errors"
|
||
"fmt"
|
||
"gitee.com/johng/gf/g/container/gtype"
|
||
"gitee.com/johng/gf/g/util/gconv"
|
||
"gitee.com/johng/gf/g/util/gregex"
|
||
"gitee.com/johng/gf/g/util/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()
|
||
)
|
||
|
||
// 给定文件的绝对路径创建文件
|
||
func Mkdir(path string) error {
|
||
err := os.MkdirAll(path, os.ModePerm)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// 给定文件的绝对路径创建文件
|
||
func Create(path string) error {
|
||
dir := Dir(path)
|
||
if !Exists(dir) {
|
||
Mkdir(dir)
|
||
}
|
||
f, err := os.Create(path)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
f.Close()
|
||
return nil
|
||
}
|
||
|
||
// 打开文件(os.O_RDWR|os.O_CREATE, 0666)
|
||
func Open(path string) (*os.File, error) {
|
||
f, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, gDEFAULT_PERM)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
return f, nil
|
||
}
|
||
|
||
// 打开文件(带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
|
||
}
|
||
|
||
// 打开文件(带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
|
||
}
|
||
|
||
// 判断所给路径文件/文件夹是否存在
|
||
func Exists(path string) bool {
|
||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
|
||
// 判断所给路径是否为文件夹
|
||
func IsDir(path string) bool {
|
||
s, err := os.Stat(path)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
return s.IsDir()
|
||
}
|
||
|
||
// 获取当前工作目录(SelfDir()方法的别名)
|
||
func Pwd() string {
|
||
return SelfDir()
|
||
}
|
||
|
||
// 判断所给路径是否为文件
|
||
func IsFile(path string) bool {
|
||
s, err := os.Stat(path)
|
||
if err != nil {
|
||
return false
|
||
}
|
||
return !s.IsDir()
|
||
}
|
||
|
||
// 获取文件或目录信息
|
||
func Info(path string) *os.FileInfo {
|
||
info, err := os.Stat(path)
|
||
if err != nil {
|
||
return nil
|
||
}
|
||
return &info
|
||
}
|
||
|
||
// 文件移动/重命名
|
||
func Move(src string, dst string) error {
|
||
return os.Rename(src, dst)
|
||
}
|
||
|
||
|
||
// 文件移动/重命名
|
||
func Rename(src string, dst string) error {
|
||
return Move(src, dst)
|
||
}
|
||
|
||
// 文件复制
|
||
func Copy(src string, dst string) error {
|
||
srcFile, err := os.Open(src)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
dstFile, err := os.Create(dst)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
_, err = io.Copy(dstFile, srcFile)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
err = dstFile.Sync()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
srcFile.Close()
|
||
dstFile.Close()
|
||
return nil
|
||
}
|
||
|
||
// 返回目录下的文件名列表
|
||
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
|
||
}
|
||
|
||
// 文件名正则匹配查找,第二个可选参数指定返回的列表是否仅为文件名(非绝对路径),默认返回绝对路径
|
||
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
|
||
}
|
||
}
|
||
|
||
// 文件/目录删除
|
||
func Remove(path string) error {
|
||
return os.RemoveAll(path)
|
||
}
|
||
|
||
// 文件是否可读
|
||
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
|
||
}
|
||
|
||
// 文件是否可写
|
||
func IsWritable(path string) bool {
|
||
result := true
|
||
if IsDir(path) {
|
||
// 如果是目录,那么创建一个临时文件进行写入测试
|
||
tfile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano())
|
||
err := Create(tfile)
|
||
if err != nil || !Exists(tfile){
|
||
result = false
|
||
} else {
|
||
Remove(tfile)
|
||
}
|
||
} else {
|
||
// 如果是文件,那么判断文件是否可打开
|
||
file, err := os.OpenFile(path, os.O_WRONLY, gDEFAULT_PERM)
|
||
if err != nil {
|
||
result = false
|
||
}
|
||
file.Close()
|
||
}
|
||
return result
|
||
}
|
||
|
||
// 修改文件/目录权限
|
||
func Chmod(path string, mode os.FileMode) error {
|
||
return os.Chmod(path, mode)
|
||
}
|
||
|
||
// 打开目录,并返回其下一级文件列表(绝对路径),按照文件名称大小写进行排序,支持目录递归遍历。
|
||
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
|
||
}
|
||
|
||
// 将所给定的路径转换为绝对路径
|
||
// 并判断文件路径是否存在,如果文件不存在,那么返回空字符串
|
||
func RealPath(path string) string {
|
||
p, err := filepath.Abs(path)
|
||
if err != nil {
|
||
return ""
|
||
}
|
||
if !Exists(p) {
|
||
return ""
|
||
}
|
||
return p
|
||
}
|
||
|
||
|
||
// 获取当前执行文件的绝对路径
|
||
func SelfPath() string {
|
||
p, _ := filepath.Abs(os.Args[0])
|
||
return p
|
||
}
|
||
|
||
// 获取当前执行文件的目录绝对路径
|
||
func SelfDir() string {
|
||
return filepath.Dir(SelfPath())
|
||
}
|
||
|
||
// 获取指定文件路径的文件名称
|
||
func Basename(path string) string {
|
||
return filepath.Base(path)
|
||
}
|
||
|
||
// 获取指定文件路径的目录地址绝对路径
|
||
func Dir(path string) string {
|
||
return filepath.Dir(path)
|
||
}
|
||
|
||
// 获取指定文件路径的文件扩展名(包含"."号)
|
||
func Ext(path string) string {
|
||
return filepath.Ext(path)
|
||
}
|
||
|
||
// 获取用户主目录
|
||
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
|
||
}
|
||
|
||
// 获取入口函数文件所在目录(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 ""
|
||
}
|
||
|
||
// 系统临时目录
|
||
func TempDir() string {
|
||
return os.TempDir()
|
||
} |