github.com/gogf/gf/v2@v2.7.4/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 "os" 12 "os/exec" 13 "path/filepath" 14 "strings" 15 "time" 16 17 "github.com/gogf/gf/v2/container/gtype" 18 "github.com/gogf/gf/v2/errors/gerror" 19 "github.com/gogf/gf/v2/text/gstr" 20 "github.com/gogf/gf/v2/util/gconv" 21 ) 22 23 const ( 24 // Separator for file system. 25 // It here defines the separator as variable 26 // to allow it modified by developer if necessary. 27 Separator = string(filepath.Separator) 28 29 // DefaultPermOpen is the default perm for file opening. 30 DefaultPermOpen = os.FileMode(0666) 31 32 // DefaultPermCopy is the default perm for file/folder copy. 33 DefaultPermCopy = os.FileMode(0755) 34 ) 35 36 var ( 37 // The absolute file path for main package. 38 // It can be only checked and set once. 39 mainPkgPath = gtype.NewString() 40 41 // selfPath is the current running binary path. 42 // As it is most commonly used, it is so defined as an internal package variable. 43 selfPath = "" 44 ) 45 46 func init() { 47 // Initialize internal package variable: selfPath. 48 selfPath, _ = exec.LookPath(os.Args[0]) 49 if selfPath != "" { 50 selfPath, _ = filepath.Abs(selfPath) 51 } 52 if selfPath == "" { 53 selfPath, _ = filepath.Abs(os.Args[0]) 54 } 55 } 56 57 // Mkdir creates directories recursively with given `path`. 58 // The parameter `path` is suggested to be an absolute path instead of relative one. 59 func Mkdir(path string) (err error) { 60 if err = os.MkdirAll(path, os.ModePerm); err != nil { 61 err = gerror.Wrapf(err, `os.MkdirAll failed for path "%s" with perm "%d"`, path, os.ModePerm) 62 return err 63 } 64 return nil 65 } 66 67 // Create creates a file with given `path` recursively. 68 // The parameter `path` is suggested to be absolute path. 69 func Create(path string) (*os.File, error) { 70 dir := Dir(path) 71 if !Exists(dir) { 72 if err := Mkdir(dir); err != nil { 73 return nil, err 74 } 75 } 76 file, err := os.Create(path) 77 if err != nil { 78 err = gerror.Wrapf(err, `os.Create failed for name "%s"`, path) 79 } 80 return file, err 81 } 82 83 // Open opens file/directory READONLY. 84 func Open(path string) (*os.File, error) { 85 file, err := os.Open(path) 86 if err != nil { 87 err = gerror.Wrapf(err, `os.Open failed for name "%s"`, path) 88 } 89 return file, err 90 } 91 92 // OpenFile opens file/directory with custom `flag` and `perm`. 93 // The parameter `flag` is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc. 94 func OpenFile(path string, flag int, perm os.FileMode) (*os.File, error) { 95 file, err := os.OpenFile(path, flag, perm) 96 if err != nil { 97 err = gerror.Wrapf(err, `os.OpenFile failed with name "%s", flag "%d", perm "%d"`, path, flag, perm) 98 } 99 return file, err 100 } 101 102 // OpenWithFlag opens file/directory with default perm and custom `flag`. 103 // The default `perm` is 0666. 104 // The parameter `flag` is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc. 105 func OpenWithFlag(path string, flag int) (*os.File, error) { 106 file, err := OpenFile(path, flag, DefaultPermOpen) 107 if err != nil { 108 return nil, err 109 } 110 return file, nil 111 } 112 113 // OpenWithFlagPerm opens file/directory with custom `flag` and `perm`. 114 // The parameter `flag` is like: O_RDONLY, O_RDWR, O_RDWR|O_CREATE|O_TRUNC, etc. 115 // The parameter `perm` is like: 0600, 0666, 0777, etc. 116 func OpenWithFlagPerm(path string, flag int, perm os.FileMode) (*os.File, error) { 117 file, err := OpenFile(path, flag, perm) 118 if err != nil { 119 return nil, err 120 } 121 return file, nil 122 } 123 124 // Join joins string array paths with file separator of current system. 125 func Join(paths ...string) string { 126 var s string 127 for _, path := range paths { 128 if s != "" { 129 s += Separator 130 } 131 s += gstr.TrimRight(path, Separator) 132 } 133 return s 134 } 135 136 // Exists checks whether given `path` exist. 137 func Exists(path string) bool { 138 if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) { 139 return true 140 } 141 return false 142 } 143 144 // IsDir checks whether given `path` a directory. 145 // Note that it returns false if the `path` does not exist. 146 func IsDir(path string) bool { 147 s, err := os.Stat(path) 148 if err != nil { 149 return false 150 } 151 return s.IsDir() 152 } 153 154 // Pwd returns absolute path of current working directory. 155 // Note that it returns an empty string if retrieving current 156 // working directory failed. 157 func Pwd() string { 158 path, err := os.Getwd() 159 if err != nil { 160 return "" 161 } 162 return path 163 } 164 165 // Chdir changes the current working directory to the named directory. 166 // If there is an error, it will be of type *PathError. 167 func Chdir(dir string) (err error) { 168 err = os.Chdir(dir) 169 if err != nil { 170 err = gerror.Wrapf(err, `os.Chdir failed with dir "%s"`, dir) 171 } 172 return 173 } 174 175 // IsFile checks whether given `path` a file, which means it's not a directory. 176 // Note that it returns false if the `path` does not exist. 177 func IsFile(path string) bool { 178 s, err := Stat(path) 179 if err != nil { 180 return false 181 } 182 return !s.IsDir() 183 } 184 185 // Stat returns a FileInfo describing the named file. 186 // If there is an error, it will be of type *PathError. 187 func Stat(path string) (os.FileInfo, error) { 188 info, err := os.Stat(path) 189 if err != nil { 190 err = gerror.Wrapf(err, `os.Stat failed for file "%s"`, path) 191 } 192 return info, err 193 } 194 195 // Move renames (moves) `src` to `dst` path. 196 // If `dst` already exists and is not a directory, it'll be replaced. 197 func Move(src string, dst string) (err error) { 198 err = os.Rename(src, dst) 199 if err != nil { 200 err = gerror.Wrapf(err, `os.Rename failed from "%s" to "%s"`, src, dst) 201 } 202 return 203 } 204 205 // Rename is alias of Move. 206 // See Move. 207 func Rename(src string, dst string) error { 208 return Move(src, dst) 209 } 210 211 // DirNames returns sub-file names of given directory `path`. 212 // Note that the returned names are NOT absolute paths. 213 func DirNames(path string) ([]string, error) { 214 f, err := Open(path) 215 if err != nil { 216 return nil, err 217 } 218 list, err := f.Readdirnames(-1) 219 _ = f.Close() 220 if err != nil { 221 err = gerror.Wrapf(err, `Read dir files failed from path "%s"`, path) 222 return nil, err 223 } 224 return list, nil 225 } 226 227 // Glob returns the names of all files matching pattern or nil 228 // if there is no matching file. The syntax of patterns is the same 229 // as in Match. The pattern may describe hierarchical names such as 230 // /usr/*/bin/ed (assuming the Separator is '/'). 231 // 232 // Glob ignores file system errors such as I/O errors reading directories. 233 // The only possible returned error is ErrBadPattern, when pattern 234 // is malformed. 235 func Glob(pattern string, onlyNames ...bool) ([]string, error) { 236 list, err := filepath.Glob(pattern) 237 if err != nil { 238 err = gerror.Wrapf(err, `filepath.Glob failed for pattern "%s"`, pattern) 239 return nil, err 240 } 241 if len(onlyNames) > 0 && onlyNames[0] && len(list) > 0 { 242 array := make([]string, len(list)) 243 for k, v := range list { 244 array[k] = Basename(v) 245 } 246 return array, nil 247 } 248 return list, nil 249 } 250 251 // Remove deletes all file/directory with `path` parameter. 252 // If parameter `path` is directory, it deletes it recursively. 253 // 254 // It does nothing if given `path` does not exist or is empty. 255 func Remove(path string) (err error) { 256 // It does nothing if `path` is empty. 257 if path == "" { 258 return nil 259 } 260 if err = os.RemoveAll(path); err != nil { 261 err = gerror.Wrapf(err, `os.RemoveAll failed for path "%s"`, path) 262 } 263 return 264 } 265 266 // IsReadable checks whether given `path` is readable. 267 func IsReadable(path string) bool { 268 result := true 269 file, err := os.OpenFile(path, os.O_RDONLY, DefaultPermOpen) 270 if err != nil { 271 result = false 272 } 273 file.Close() 274 return result 275 } 276 277 // IsWritable checks whether given `path` is writable. 278 // 279 // TODO improve performance; use golang.org/x/sys to cross-plat-form 280 func IsWritable(path string) bool { 281 result := true 282 if IsDir(path) { 283 // If it's a directory, create a temporary file to test whether it's writable. 284 tmpFile := strings.TrimRight(path, Separator) + Separator + gconv.String(time.Now().UnixNano()) 285 if f, err := Create(tmpFile); err != nil || !Exists(tmpFile) { 286 result = false 287 } else { 288 _ = f.Close() 289 _ = Remove(tmpFile) 290 } 291 } else { 292 // If it's a file, check if it can open it. 293 file, err := os.OpenFile(path, os.O_WRONLY, DefaultPermOpen) 294 if err != nil { 295 result = false 296 } 297 _ = file.Close() 298 } 299 return result 300 } 301 302 // Chmod is alias of os.Chmod. 303 // See os.Chmod. 304 func Chmod(path string, mode os.FileMode) (err error) { 305 err = os.Chmod(path, mode) 306 if err != nil { 307 err = gerror.Wrapf(err, `os.Chmod failed with path "%s" and mode "%s"`, path, mode) 308 } 309 return 310 } 311 312 // Abs returns an absolute representation of path. 313 // If the path is not absolute it will be joined with the current 314 // working directory to turn it into an absolute path. The absolute 315 // path name for a given file is not guaranteed to be unique. 316 // Abs calls Clean on the result. 317 func Abs(path string) string { 318 p, _ := filepath.Abs(path) 319 return p 320 } 321 322 // RealPath converts the given `path` to its absolute path 323 // and checks if the file path exists. 324 // If the file does not exist, return an empty string. 325 func RealPath(path string) string { 326 p, err := filepath.Abs(path) 327 if err != nil { 328 return "" 329 } 330 if !Exists(p) { 331 return "" 332 } 333 return p 334 } 335 336 // SelfPath returns absolute file path of current running process(binary). 337 func SelfPath() string { 338 return selfPath 339 } 340 341 // SelfName returns file name of current running process(binary). 342 func SelfName() string { 343 return Basename(SelfPath()) 344 } 345 346 // SelfDir returns absolute directory path of current running process(binary). 347 func SelfDir() string { 348 return filepath.Dir(SelfPath()) 349 } 350 351 // Basename returns the last element of path, which contains file extension. 352 // Trailing path separators are removed before extracting the last element. 353 // If the path is empty, Base returns ".". 354 // If the path consists entirely of separators, Basename returns a single separator. 355 // 356 // Example: 357 // Basename("/var/www/file.js") -> file.js 358 // Basename("file.js") -> file.js 359 func Basename(path string) string { 360 return filepath.Base(path) 361 } 362 363 // Name returns the last element of path without file extension. 364 // 365 // Example: 366 // Name("/var/www/file.js") -> file 367 // Name("file.js") -> file 368 func Name(path string) string { 369 base := filepath.Base(path) 370 if i := strings.LastIndexByte(base, '.'); i != -1 { 371 return base[:i] 372 } 373 return base 374 } 375 376 // Dir returns all but the last element of path, typically the path's directory. 377 // After dropping the final element, Dir calls Clean on the path and trailing 378 // slashes are removed. 379 // If the `path` is empty, Dir returns ".". 380 // If the `path` is ".", Dir treats the path as current working directory. 381 // If the `path` consists entirely of separators, Dir returns a single separator. 382 // The returned path does not end in a separator unless it is the root directory. 383 // 384 // Example: 385 // Dir("/var/www/file.js") -> "/var/www" 386 // Dir("file.js") -> "." 387 func Dir(path string) string { 388 if path == "." { 389 return filepath.Dir(RealPath(path)) 390 } 391 return filepath.Dir(path) 392 } 393 394 // IsEmpty checks whether the given `path` is empty. 395 // If `path` is a folder, it checks if there's any file under it. 396 // If `path` is a file, it checks if the file size is zero. 397 // 398 // Note that it returns true if `path` does not exist. 399 func IsEmpty(path string) bool { 400 stat, err := Stat(path) 401 if err != nil { 402 return true 403 } 404 if stat.IsDir() { 405 file, err := os.Open(path) 406 if err != nil { 407 return true 408 } 409 defer file.Close() 410 names, err := file.Readdirnames(-1) 411 if err != nil { 412 return true 413 } 414 return len(names) == 0 415 } else { 416 return stat.Size() == 0 417 } 418 } 419 420 // Ext returns the file name extension used by path. 421 // The extension is the suffix beginning at the final dot 422 // in the final element of path; it is empty if there is 423 // no dot. 424 // Note: the result contains symbol '.'. 425 // 426 // Example: 427 // Ext("main.go") => .go 428 // Ext("api.json") => .json 429 func Ext(path string) string { 430 ext := filepath.Ext(path) 431 if p := strings.IndexByte(ext, '?'); p != -1 { 432 ext = ext[0:p] 433 } 434 return ext 435 } 436 437 // ExtName is like function Ext, which returns the file name extension used by path, 438 // but the result does not contain symbol '.'. 439 // 440 // Example: 441 // ExtName("main.go") => go 442 // ExtName("api.json") => json 443 func ExtName(path string) string { 444 return strings.TrimLeft(Ext(path), ".") 445 } 446 447 // Temp retrieves and returns the temporary directory of current system. 448 // 449 // The optional parameter `names` specifies the sub-folders/sub-files, 450 // which will be joined with current system separator and returned with the path. 451 func Temp(names ...string) string { 452 path := os.TempDir() 453 for _, name := range names { 454 path = Join(path, name) 455 } 456 return path 457 }