mirror of
https://github.com/duke-git/lancet.git
synced 2025-10-29 18:01:55 +08:00
361 lines
7.0 KiB
Go
361 lines
7.0 KiB
Go
// 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"
|
|
"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
|
|
}
|
|
|
|
// 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: todo
|
|
func CurrentPath() string {
|
|
var absPath string
|
|
_, filename, _, ok := runtime.Caller(1)
|
|
if ok {
|
|
absPath = path.Dir(filename)
|
|
}
|
|
|
|
return absPath
|
|
}
|