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 }