package file import ( "bufio" "errors" "fmt" "io" "io/fs" "io/ioutil" "log" "mime/multipart" "os" "path" path2 "path" "path/filepath" "strconv" "strings" "github.com/mholt/archiver/v3" ) // GetSize get the file size func GetSize(f multipart.File) (int, error) { content, err := ioutil.ReadAll(f) return len(content), err } // GetExt get the file ext func GetExt(fileName string) string { return path.Ext(fileName) } // CheckNotExist check if the file exists func CheckNotExist(src string) bool { _, err := os.Stat(src) return os.IsNotExist(err) } // CheckPermission check if the file has permission func CheckPermission(src string) bool { _, err := os.Stat(src) return os.IsPermission(err) } // IsNotExistMkDir create a directory if it does not exist func IsNotExistMkDir(src string) error { if notExist := CheckNotExist(src); notExist { if err := MkDir(src); err != nil { return err } } return nil } // MkDir create a directory func MkDir(src string) error { err := os.MkdirAll(src, os.ModePerm) if err != nil { return err } os.Chmod(src, 0o777) return nil } // RMDir remove a directory func RMDir(src string) error { err := os.RemoveAll(src) if err != nil { return err } os.Remove(src) return nil } // Open a file according to a specific mode func Open(name string, flag int, perm os.FileMode) (*os.File, error) { f, err := os.OpenFile(name, flag, perm) if err != nil { return nil, err } return f, nil } // MustOpen maximize trying to open the file func MustOpen(fileName, filePath string) (*os.File, error) { //dir, err := os.Getwd() //if err != nil { // return nil, fmt.Errorf("os.Getwd err: %v", err) //} src := filePath perm := CheckPermission(src) if perm == true { return nil, fmt.Errorf("file.CheckPermission Permission denied src: %s", src) } err := IsNotExistMkDir(src) if err != nil { return nil, fmt.Errorf("file.IsNotExistMkDir src: %s, err: %v", src, err) } f, err := Open(src+fileName, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0o644) if err != nil { return nil, fmt.Errorf("Fail to OpenFile :%v", err) } return f, nil } // 判断所给路径文件/文件夹是否存在 func Exists(path string) bool { _, err := os.Stat(path) // os.Stat获取文件信息 if err != nil { if os.IsExist(err) { return true } return false } return true } // 判断所给路径是否为文件夹 func IsDir(path string) bool { s, err := os.Stat(path) if err != nil { return false } return s.IsDir() } // 判断所给路径是否为文件 func IsFile(path string) bool { return !IsDir(path) } func CreateFile(path string) error { file, err := os.Create(path) if err != nil { return err } defer file.Close() return nil } func CreateFileAndWriteContent(path string, content string) error { file, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE, 0o666) if err != nil { return err } defer file.Close() write := bufio.NewWriter(file) write.WriteString(content) write.Flush() return nil } // IsNotExistMkDir create a directory if it does not exist func IsNotExistCreateFile(src string) error { if notExist := CheckNotExist(src); notExist { if err := CreateFile(src); err != nil { return err } } return nil } func ReadFullFile(path string) []byte { file, err := os.Open(path) if err != nil { return []byte("") } defer file.Close() content, err := ioutil.ReadAll(file) if err != nil { return []byte("") } return content } // File copies a single file from src to dst func CopyFile(src, dst, style string) error { var err error var srcfd *os.File var dstfd *os.File var srcinfo os.FileInfo lastPath := src[strings.LastIndex(src, "/")+1:] if !strings.HasSuffix(dst, "/") { dst += "/" } dst += lastPath if Exists(dst) { if style == "skip" { return nil } else { os.Remove(dst) } } if srcfd, err = os.Open(src); err != nil { return err } defer srcfd.Close() if dstfd, err = os.Create(dst); err != nil { return err } defer dstfd.Close() if _, err = io.Copy(dstfd, srcfd); err != nil { return err } if srcinfo, err = os.Stat(src); err != nil { return err } return os.Chmod(dst, srcinfo.Mode()) } /** * @description: * @param {*} src * @param {*} dst * @param {string} style * @return {*} * @method: * @router: */ func CopySingleFile(src, dst, style string) error { var err error var srcfd *os.File var dstfd *os.File var srcinfo os.FileInfo if Exists(dst) { if style == "skip" { return nil } else { os.Remove(dst) } } if srcfd, err = os.Open(src); err != nil { return err } defer srcfd.Close() if dstfd, err = os.Create(dst); err != nil { return err } defer dstfd.Close() if _, err = io.Copy(dstfd, srcfd); err != nil { return err } if srcinfo, err = os.Stat(src); err != nil { return err } return os.Chmod(dst, srcinfo.Mode()) } // Check for duplicate file names func GetNoDuplicateFileName(fullPath string) string { path, fileName := filepath.Split(fullPath) fileSuffix := path2.Ext(fileName) filenameOnly := strings.TrimSuffix(fileName, fileSuffix) for i := 0; Exists(fullPath); i++ { fullPath = path2.Join(path, filenameOnly+"("+strconv.Itoa(i+1)+")"+fileSuffix) } return fullPath } // Dir copies a whole directory recursively func CopyDir(src string, dst string, style string) error { var err error var fds []os.FileInfo var srcinfo os.FileInfo if srcinfo, err = os.Stat(src); err != nil { return err } if !srcinfo.IsDir() { if err = CopyFile(src, dst, style); err != nil { fmt.Println(err) } return nil } // dstPath := dst lastPath := src[strings.LastIndex(src, "/")+1:] dst += "/" + lastPath // for i := 0; Exists(dst); i++ { // dst = dstPath + "/" + lastPath + strconv.Itoa(i+1) // } if Exists(dst) { if style == "skip" { return nil } else { os.Remove(dst) } } if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil { return err } if fds, err = ioutil.ReadDir(src); err != nil { return err } for _, fd := range fds { srcfp := path.Join(src, fd.Name()) dstfp := dst // path.Join(dst, fd.Name()) if fd.IsDir() { if err = CopyDir(srcfp, dstfp, style); err != nil { fmt.Println(err) } } else { if err = CopyFile(srcfp, dstfp, style); err != nil { fmt.Println(err) } } } return nil } func WriteToPath(data []byte, path, name string) error { fullPath := path if strings.HasSuffix(path, "/") { fullPath += name } else { fullPath += "/" + name } return WriteToFullPath(data, fullPath, 0o666) } func WriteToFullPath(data []byte, fullPath string, perm fs.FileMode) error { if err := IsNotExistCreateFile(fullPath); err != nil { return err } file, err := os.OpenFile(fullPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, perm, ) if err != nil { return err } defer file.Close() _, err = file.Write(data) return err } // 最终拼接 func SpliceFiles(dir, path string, length int, startPoint int) error { fullPath := path if err := IsNotExistCreateFile(fullPath); err != nil { return err } file, _ := os.OpenFile(fullPath, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0o666, ) defer file.Close() bufferedWriter := bufio.NewWriter(file) // todo: here should have a goroutine to remove each partial file after it is read, to save disk space for i := 0; i < length+startPoint-1; i++ { data, err := ioutil.ReadFile(dir + "/" + strconv.Itoa(i+startPoint)) if err != nil { return err } if _, err := bufferedWriter.Write(data); err != nil { // recommend to use https://github.com/iceber/iouring-go for faster write return err } } bufferedWriter.Flush() return nil } func GetCompressionAlgorithm(t string) (string, archiver.Writer, error) { switch t { case "zip", "": return ".zip", archiver.NewZip(), nil case "tar": return ".tar", archiver.NewTar(), nil case "targz": return ".tar.gz", archiver.NewTarGz(), nil case "tarbz2": return ".tar.bz2", archiver.NewTarBz2(), nil case "tarxz": return ".tar.xz", archiver.NewTarXz(), nil case "tarlz4": return ".tar.lz4", archiver.NewTarLz4(), nil case "tarsz": return ".tar.sz", archiver.NewTarSz(), nil default: return "", nil, errors.New("format not implemented") } } func AddFile(ar archiver.Writer, path, commonPath string) error { info, err := os.Stat(path) if err != nil { return err } if !info.IsDir() && !info.Mode().IsRegular() { return nil } file, err := os.Open(path) if err != nil { return err } defer file.Close() if path != commonPath { filename := info.Name() err = ar.Write(archiver.File{ FileInfo: archiver.FileInfo{ FileInfo: info, CustomName: filename, }, ReadCloser: file, }) if err != nil { return err } } if info.IsDir() { names, err := file.Readdirnames(0) if err != nil { return err } for _, name := range names { err = AddFile(ar, filepath.Join(path, name), commonPath) if err != nil { log.Printf("Failed to archive %v", err) } } } return nil } func CommonPrefix(sep byte, paths ...string) string { // Handle special cases. switch len(paths) { case 0: return "" case 1: return path.Clean(paths[0]) } // Note, we treat string as []byte, not []rune as is often // done in Go. (And sep as byte, not rune). This is because // most/all supported OS' treat paths as string of non-zero // bytes. A filename may be displayed as a sequence of Unicode // runes (typically encoded as UTF-8) but paths are // not required to be valid UTF-8 or in any normalized form // (e.g. "é" (U+00C9) and "é" (U+0065,U+0301) are different // file names. c := []byte(path.Clean(paths[0])) // We add a trailing sep to handle the case where the // common prefix directory is included in the path list // (e.g. /home/user1, /home/user1/foo, /home/user1/bar). // path.Clean will have cleaned off trailing / separators with // the exception of the root directory, "/" (in which case we // make it "//", but this will get fixed up to "/" bellow). c = append(c, sep) // Ignore the first path since it's already in c for _, v := range paths[1:] { // Clean up each path before testing it v = path.Clean(v) + string(sep) // Find the first non-common byte and truncate c if len(v) < len(c) { c = c[:len(v)] } for i := 0; i < len(c); i++ { if v[i] != c[i] { c = c[:i] break } } } // Remove trailing non-separator characters and the final separator for i := len(c) - 1; i >= 0; i-- { if c[i] == sep { c = c[:i] break } } return string(c) } func GetFileOrDirSize(path string) (int64, error) { fileInfo, err := os.Stat(path) if err != nil { return 0, err } if fileInfo.IsDir() { return DirSizeB(path + "/") } return fileInfo.Size(), nil } // getFileSize get file size by path(B) func DirSizeB(path string) (int64, error) { var size int64 err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error { if !info.IsDir() { size += info.Size() } return err }) return size, err } func MoveFile(sourcePath, destPath string) error { inputFile, err := os.Open(sourcePath) if err != nil { return fmt.Errorf("Couldn't open source file: %s", err) } outputFile, err := os.Create(destPath) if err != nil { inputFile.Close() return fmt.Errorf("Couldn't open dest file: %s", err) } defer outputFile.Close() _, err = io.Copy(outputFile, inputFile) inputFile.Close() if err != nil { return fmt.Errorf("Writing to output file failed: %s", err) } err = os.Remove(sourcePath) if err != nil { return fmt.Errorf("Failed removing original file: %s", err) } return nil } func ReadLine(lineNumber int, path string) string { file, err := os.Open(path) if err != nil { return "" } fileScanner := bufio.NewScanner(file) lineCount := 1 for fileScanner.Scan() { if lineCount == lineNumber { return fileScanner.Text() } lineCount++ } defer file.Close() return "" }