gf/g/os/gfile/gfile.go
2018-12-16 22:22:07 +08:00

422 lines
10 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://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()
}