From 4e5d76af37053aaca05a20eceb846f31d1dca5c3 Mon Sep 17 00:00:00 2001 From: kenneth Date: Tue, 10 May 2022 15:09:33 +0800 Subject: [PATCH] add file utils --- pkg/fileutil/file.go | 314 ++++++++++++++++++++++++++++++++++++++ pkg/fileutil/file_test.go | 177 +++++++++++++++++++++ pkg/ft/file.go | 34 ----- 3 files changed, 491 insertions(+), 34 deletions(-) create mode 100644 pkg/fileutil/file.go create mode 100644 pkg/fileutil/file_test.go delete mode 100644 pkg/ft/file.go diff --git a/pkg/fileutil/file.go b/pkg/fileutil/file.go new file mode 100644 index 0000000..8dd4def --- /dev/null +++ b/pkg/fileutil/file.go @@ -0,0 +1,314 @@ +// Package fileutil 实现了一些文件操作的基本功能 +package fileutil + +import ( + "archive/zip" + "bufio" + "errors" + "io" + "io/fs" + "io/ioutil" + "net/http" + "os" + "path/filepath" + "strings" +) + +// IsExist 检查一个文件或目录是否存在 +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 在路径中创建一个文件 +func CreateFile(path string) bool { + file, err := os.Create(path) + if err != nil { + return false + } + + defer file.Close() + return true +} + +// IsDir 检查路径是否为目录 +func IsDir(path string) bool { + file, err := os.Stat(path) + if err != nil { + return false + } + return file.IsDir() +} + +// RemoveFile 删除路径中的文件 +func RemoveFile(path string) error { + return os.Remove(path) +} + +// CopyFile 复制src文件到dest文件 +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) + distFile.Write(tmp[:n]) + if err != nil { + if err == io.EOF { + return nil + } + return err + } + } +} + +//ClearFile 在路径文件中写入空字符串 +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 返回文件内容的字符串 +func ReadFileToString(path string) (string, error) { + bytes, err := ioutil.ReadFile(path) + if err != nil { + return "", err + } + return string(bytes), nil +} + +// ReadFileByLine 逐行读取文件 +func ReadFileByLine(path string) ([]string, error) { + f, err := os.Open(path) + if err != nil { + return nil, err + } + defer f.Close() + + res := make([]string, 0) + buf := bufio.NewReader(f) + + for { + line, _, err := buf.ReadLine() + l := string(line) + if err == io.EOF { + break + } + if err != nil { + continue + } + res = append(res, l) + } + + return res, nil +} + +// ListFileNames 返回路径中的所有文件名 +func ListFileNames(path string) ([]string, error) { + if !IsExist(path) { + return []string{}, nil + } + + fs, err := ioutil.ReadDir(path) + if err != nil { + return []string{}, err + } + + sz := len(fs) + if sz == 0 { + return []string{}, nil + } + + res := []string{} + for i := 0; i < sz; i++ { + if !fs[i].IsDir() { + res = append(res, fs[i].Name()) + } + } + + return res, nil +} + +// Zip 创建压缩文件, 参数`filePath`可以是一个单独的文件或一个目录 +func Zip(filePath string, destPath string) error { + zipFile, err := os.Create(destPath) + if err != nil { + return err + } + defer zipFile.Close() + + archive := zip.NewWriter(zipFile) + defer archive.Close() + + filepath.Walk(filePath, 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(filePath)+"/") + + 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 + }) + + return nil +} + +// UnZip 解压缩文件并将其保存到destPath +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 { + path := filepath.Join(destPath, f.Name) + if f.FileInfo().IsDir() { + os.MkdirAll(path, os.ModePerm) + } else { + if err = os.MkdirAll(filepath.Dir(path), os.ModePerm); 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 +} + +// IsLink 检查一个文件是否是符号链接 +func IsLink(path string) bool { + fi, err := os.Lstat(path) + if err != nil { + return false + } + return fi.Mode()&os.ModeSymlink != 0 +} + +// FileMode 返回文件的模式和权限 +func FileMode(path string) (fs.FileMode, error) { + fi, err := os.Lstat(path) + if err != nil { + return 0, err + } + return fi.Mode(), nil +} + +// MiMeType 返回文件的mime类型 +// 参数 `file`应该是字符串(文件路径)或*os.File +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 +} + +// IsNotExistMkDir 如果不存在则新建文件夹 +func IsNotExistMkDir(src string) error { + if notExist := IsExist(src); !notExist { + if err := MkDir(src); err != nil { + return err + } + } + return nil +} + +// MkDir 新建文件夹 +func MkDir(src string) error { + err := os.MkdirAll(src, os.ModePerm) + if err != nil { + return err + } + return nil +} diff --git a/pkg/fileutil/file_test.go b/pkg/fileutil/file_test.go new file mode 100644 index 0000000..8c3306f --- /dev/null +++ b/pkg/fileutil/file_test.go @@ -0,0 +1,177 @@ +package fileutil + +import ( + "os" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestIsExist(t *testing.T) { + cases := []string{"./", "./file.go", "./a.txt"} + expected := []bool{true, true, false} + + for i := 0; i < len(cases); i++ { + actual := IsExist(cases[i]) + require.Equal(t, expected[i], actual) + } +} + +func TestCreateFile(t *testing.T) { + f := "./text.txt" + if CreateFile(f) { + file, err := os.Open(f) + require.Nil(t, err) + require.Equal(t, f, file.Name()) + } else { + t.FailNow() + } + os.Remove(f) +} + +func TestIsDir(t *testing.T) { + cases := []string{"./", "./a.txt"} + expected := []bool{true, false} + + for i := 0; i < len(cases); i++ { + actual := IsDir(cases[i]) + require.Equal(t, expected[i], actual) + } +} + +func TestRemoveFile(t *testing.T) { + f := "./text.txt" + if !IsExist(f) { + CreateFile(f) + err := RemoveFile(f) + require.Nil(t, err) + } +} + +func TestCopyFile(t *testing.T) { + srcFile := "./text.txt" + CreateFile(srcFile) + + destFile := "./text_copy.txt" + + err := CopyFile(srcFile, destFile) + if err != nil { + file, err := os.Open(destFile) + require.Nil(t, err) + require.Equal(t, destFile, file.Name()) + } + os.Remove(srcFile) + os.Remove(destFile) +} + +func TestListFileNames(t *testing.T) { + filesInPath, err := ListFileNames("./") + require.Nil(t, err) + + expected := []string{"file.go", "file_test.go"} + require.Equal(t, expected, filesInPath) +} + +func TestReadFileToString(t *testing.T) { + path := "./text.txt" + CreateFile(path) + + f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) + f.WriteString("hello world") + + content, _ := ReadFileToString(path) + require.Equal(t, "hello world", content) + + os.Remove(path) +} + +func TestClearFile(t *testing.T) { + path := "./text.txt" + CreateFile(path) + + f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + + f.WriteString("hello world") + + err := ClearFile(path) + require.Nil(t, err) + + content, _ := ReadFileToString(path) + require.Equal(t, "", content) + + os.Remove(path) +} + +func TestReadFileByLine(t *testing.T) { + path := "./text.txt" + CreateFile(path) + + f, _ := os.OpenFile(path, os.O_WRONLY|os.O_TRUNC, 0777) + defer f.Close() + f.WriteString("hello\nworld") + + expected := []string{"hello", "world"} + actual, _ := ReadFileByLine(path) + require.Equal(t, expected, actual) + + os.Remove(path) +} + +func TestZipAndUnZip(t *testing.T) { + srcFile := "./text.txt" + CreateFile(srcFile) + + file, _ := os.OpenFile(srcFile, os.O_WRONLY|os.O_TRUNC, 0777) + defer file.Close() + file.WriteString("hello\nworld") + + zipFile := "./text.zip" + err := Zip(srcFile, zipFile) + require.Nil(t, err) + + unZipPath := "./unzip" + err = UnZip(zipFile, unZipPath) + require.Nil(t, err) + + unZipFile := "./unzip/text.txt" + require.Equal(t, true, IsExist(unZipFile)) + + os.Remove(srcFile) + os.Remove(zipFile) + os.RemoveAll(unZipPath) +} + +func TestFileMode(t *testing.T) { + srcFile := "./text.txt" + CreateFile(srcFile) + + mode, err := FileMode(srcFile) + require.Nil(t, err) + + t.Log(mode) + + os.Remove(srcFile) +} + +func TestIsLink(t *testing.T) { + srcFile := "./text.txt" + CreateFile(srcFile) + + linkFile := "./text.link" + if !IsExist(linkFile) { + _ = os.Symlink(srcFile, linkFile) + } + require.Equal(t, true, IsLink(linkFile)) + + require.Equal(t, false, IsLink("./file.go")) + + os.Remove(srcFile) + os.Remove(linkFile) +} + +func TestMiMeType(t *testing.T) { + f, _ := os.Open("./file.go") + require.Equal(t, "text/plain; charset=utf-8", MiMeType(f)) + require.Equal(t, "text/plain; charset=utf-8", MiMeType("./file.go")) +} diff --git a/pkg/ft/file.go b/pkg/ft/file.go deleted file mode 100644 index eff42ca..0000000 --- a/pkg/ft/file.go +++ /dev/null @@ -1,34 +0,0 @@ -package ft - -import "os" - -// Exists 判断所给路径文件/文件夹是否存在 -func Exists(path string) bool { - _, err := os.Stat(path) //os.Stat获取文件信息 - if err != nil { - if os.IsExist(err) { - return true - } - return false - } - return true -} - -// IsNotExistMkDir 如果不存在则新建文件夹 -func IsNotExistMkDir(src string) error { - if notExist := Exists(src); !notExist { - if err := MkDir(src); err != nil { - return err - } - } - return nil -} - -// MkDir 新建文件夹 -func MkDir(src string) error { - err := os.MkdirAll(src, os.ModePerm) - if err != nil { - return err - } - return nil -}