// Copyright 2021 dudaodong@gmail.com. All rights reserved. // Use of this source code is governed by MIT license. // Package fileutil implements some basic functions for file operations package fileutil import ( "archive/zip" "bufio" "bytes" "errors" "fmt" "io" "io/fs" "net/http" "os" "path" "path/filepath" "runtime" "strings" ) // IsExist checks if a file or directory exists. // Play: https://go.dev/play/p/nKKXt8ZQbmh func IsExist(path string) bool { _, err := os.Stat(path) if err == nil { return true } if errors.Is(err, os.ErrNotExist) { return false } return false } // CreateFile create a file in path. // Play: https://go.dev/play/p/lDt8PEsTNKI func CreateFile(path string) bool { file, err := os.Create(path) if err != nil { return false } defer file.Close() return true } // CreateDir create directory in absolute path. param `absPath` like /a/, /a/b/. // Play: https://go.dev/play/p/qUuCe1OGQnM func CreateDir(absPath string) error { return os.MkdirAll(path.Dir(absPath), os.ModePerm) } // IsDir checks if the path is directory or not. // Play: https://go.dev/play/p/WkVwEKqtOWk func IsDir(path string) bool { file, err := os.Stat(path) if err != nil { return false } return file.IsDir() } // RemoveFile remove the path file. // Play: https://go.dev/play/p/P2y0XW8a1SH func RemoveFile(path string) error { return os.Remove(path) } // CopyFile copy src file to dest file. // Play: https://go.dev/play/p/Jg9AMJMLrJi func CopyFile(srcFilePath string, dstFilePath string) error { srcFile, err := os.Open(srcFilePath) if err != nil { return err } defer srcFile.Close() distFile, err := os.Create(dstFilePath) if err != nil { return err } defer distFile.Close() var tmp = make([]byte, 1024*4) for { n, err := srcFile.Read(tmp) if err != nil { if err == io.EOF { return nil } return err } _, err = distFile.Write(tmp[:n]) if err != nil { return err } } } // ClearFile write empty string to path file. // Play: https://go.dev/play/p/NRZ0ZT-G94H func ClearFile(path string) error { f, err := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) if err != nil { return err } defer f.Close() _, err = f.WriteString("") return err } // ReadFileToString return string of file content. // Play: https://go.dev/play/p/cmfwp_5SQTp func ReadFileToString(path string) (string, error) { bytes, err := os.ReadFile(path) if err != nil { return "", err } return string(bytes), nil } // ReadFileByLine read file line by line. // Play: https://go.dev/play/p/svJP_7ZrBrD func ReadFileByLine(path string) ([]string, error) { f, err := os.Open(path) if err != nil { return nil, err } defer f.Close() result := make([]string, 0) buf := bufio.NewReader(f) for { line, _, err := buf.ReadLine() l := string(line) if err == io.EOF { break } if err != nil { continue } result = append(result, l) } return result, nil } // ListFileNames return all file names in the path. // Play: https://go.dev/play/p/Tjd7Y07rejl func ListFileNames(path string) ([]string, error) { if !IsExist(path) { return []string{}, nil } fs, err := os.ReadDir(path) if err != nil { return []string{}, err } sz := len(fs) if sz == 0 { return []string{}, nil } result := []string{} for i := 0; i < sz; i++ { if !fs[i].IsDir() { result = append(result, fs[i].Name()) } } return result, nil } // IsZipFile checks if file is zip or not. // Play: todo func IsZipFile(filepath string) bool { f, err := os.Open(filepath) if err != nil { return false } defer f.Close() buf := make([]byte, 4) if n, err := f.Read(buf); err != nil || n < 4 { return false } return bytes.Equal(buf, []byte("PK\x03\x04")) } // Zip create zip file, fpath could be a single file or a directory. // Play: https://go.dev/play/p/j-3sWBp8ik_P func Zip(fpath string, destPath string) error { zipFile, err := os.Create(destPath) if err != nil { return err } defer zipFile.Close() archive := zip.NewWriter(zipFile) defer archive.Close() err = filepath.Walk(fpath, func(path string, info os.FileInfo, err error) error { if err != nil { return err } header, err := zip.FileInfoHeader(info) if err != nil { return err } header.Name = strings.TrimPrefix(path, filepath.Dir(fpath)+"/") if info.IsDir() { header.Name += "/" } else { header.Method = zip.Deflate } writer, err := archive.CreateHeader(header) if err != nil { return err } if !info.IsDir() { file, err := os.Open(path) if err != nil { return err } defer file.Close() _, err = io.Copy(writer, file) if err != nil { return err } } return nil }) if err != nil { return err } return nil } // UnZip unzip the file and save it to destPath. // Play: https://go.dev/play/p/g0w34kS7B8m func UnZip(zipFile string, destPath string) error { zipReader, err := zip.OpenReader(zipFile) if err != nil { return err } defer zipReader.Close() for _, f := range zipReader.File { //issue#62: fix ZipSlip bug path, err := safeFilepathJoin(destPath, f.Name) if err != nil { return err } if f.FileInfo().IsDir() { err = os.MkdirAll(path, os.ModePerm) if err != nil { return err } } else { err = os.MkdirAll(filepath.Dir(path), os.ModePerm) if err != nil { return err } inFile, err := f.Open() if err != nil { return err } defer inFile.Close() outFile, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode()) if err != nil { return err } defer outFile.Close() _, err = io.Copy(outFile, inFile) if err != nil { return err } } } return nil } func safeFilepathJoin(path1, path2 string) (string, error) { relPath, err := filepath.Rel(".", path2) if err != nil || strings.HasPrefix(relPath, "..") { return "", fmt.Errorf("(zipslip) filepath is unsafe %q: %v", path2, err) } if path1 == "" { path1 = "." } return filepath.Join(path1, filepath.Join("/", relPath)), nil } // IsLink checks if a file is symbol link or not. // Play: https://go.dev/play/p/TL-b-Kzvf44 func IsLink(path string) bool { fi, err := os.Lstat(path) if err != nil { return false } return fi.Mode()&os.ModeSymlink != 0 } // FileMode return file's mode and permission. // Play: https://go.dev/play/p/2l2hI42fA3p func FileMode(path string) (fs.FileMode, error) { fi, err := os.Lstat(path) if err != nil { return 0, err } return fi.Mode(), nil } // MiMeType return file mime type // param `file` should be string(file path) or *os.File. // Play: https://go.dev/play/p/bd5sevSUZNu func MiMeType(file any) string { var mediatype string readBuffer := func(f *os.File) ([]byte, error) { buffer := make([]byte, 512) _, err := f.Read(buffer) if err != nil { return nil, err } return buffer, nil } if filePath, ok := file.(string); ok { f, err := os.Open(filePath) if err != nil { return mediatype } buffer, err := readBuffer(f) if err != nil { return mediatype } return http.DetectContentType(buffer) } if f, ok := file.(*os.File); ok { buffer, err := readBuffer(f) if err != nil { return mediatype } return http.DetectContentType(buffer) } return mediatype } // CurrentPath return current absolute path. // Play: https://go.dev/play/p/s74a9iBGcSw func CurrentPath() string { var absPath string _, filename, _, ok := runtime.Caller(1) if ok { absPath = path.Dir(filename) } return absPath }