Spaces:
Paused
Paused
| package utils | |
| import ( | |
| "fmt" | |
| "io" | |
| "mime" | |
| "os" | |
| "path" | |
| "path/filepath" | |
| "strings" | |
| "github.com/alist-org/alist/v3/internal/errs" | |
| "github.com/alist-org/alist/v3/internal/conf" | |
| log "github.com/sirupsen/logrus" | |
| ) | |
| // CopyFile File copies a single file from src to dst | |
| func CopyFile(src, dst string) error { | |
| var err error | |
| var srcfd *os.File | |
| var dstfd *os.File | |
| var srcinfo os.FileInfo | |
| if srcfd, err = os.Open(src); err != nil { | |
| return err | |
| } | |
| defer srcfd.Close() | |
| if dstfd, err = CreateNestedFile(dst); err != nil { | |
| return err | |
| } | |
| defer dstfd.Close() | |
| if _, err = CopyWithBuffer(dstfd, srcfd); err != nil { | |
| return err | |
| } | |
| if srcinfo, err = os.Stat(src); err != nil { | |
| return err | |
| } | |
| return os.Chmod(dst, srcinfo.Mode()) | |
| } | |
| // CopyDir Dir copies a whole directory recursively | |
| func CopyDir(src, dst string) error { | |
| var err error | |
| var fds []os.DirEntry | |
| var srcinfo os.FileInfo | |
| if srcinfo, err = os.Stat(src); err != nil { | |
| return err | |
| } | |
| if err = os.MkdirAll(dst, srcinfo.Mode()); err != nil { | |
| return err | |
| } | |
| if fds, err = os.ReadDir(src); err != nil { | |
| return err | |
| } | |
| for _, fd := range fds { | |
| srcfp := path.Join(src, fd.Name()) | |
| dstfp := path.Join(dst, fd.Name()) | |
| if fd.IsDir() { | |
| if err = CopyDir(srcfp, dstfp); err != nil { | |
| fmt.Println(err) | |
| } | |
| } else { | |
| if err = CopyFile(srcfp, dstfp); err != nil { | |
| fmt.Println(err) | |
| } | |
| } | |
| } | |
| return nil | |
| } | |
| // SymlinkOrCopyFile symlinks a file or copy if symlink failed | |
| func SymlinkOrCopyFile(src, dst string) error { | |
| if err := CreateNestedDirectory(filepath.Dir(dst)); err != nil { | |
| return err | |
| } | |
| if err := os.Symlink(src, dst); err != nil { | |
| return CopyFile(src, dst) | |
| } | |
| return nil | |
| } | |
| // Exists determine whether the file exists | |
| func Exists(name string) bool { | |
| if _, err := os.Stat(name); err != nil { | |
| if os.IsNotExist(err) { | |
| return false | |
| } | |
| } | |
| return true | |
| } | |
| // CreateNestedDirectory create nested directory | |
| func CreateNestedDirectory(path string) error { | |
| err := os.MkdirAll(path, 0700) | |
| if err != nil { | |
| log.Errorf("can't create folder, %s", err) | |
| } | |
| return err | |
| } | |
| // CreateNestedFile create nested file | |
| func CreateNestedFile(path string) (*os.File, error) { | |
| basePath := filepath.Dir(path) | |
| if err := CreateNestedDirectory(basePath); err != nil { | |
| return nil, err | |
| } | |
| return os.Create(path) | |
| } | |
| // CreateTempFile create temp file from io.ReadCloser, and seek to 0 | |
| func CreateTempFile(r io.Reader, size int64) (*os.File, error) { | |
| if f, ok := r.(*os.File); ok { | |
| return f, nil | |
| } | |
| f, err := os.CreateTemp(conf.Conf.TempDir, "file-*") | |
| if err != nil { | |
| return nil, err | |
| } | |
| readBytes, err := CopyWithBuffer(f, r) | |
| if err != nil { | |
| _ = os.Remove(f.Name()) | |
| return nil, errs.NewErr(err, "CreateTempFile failed") | |
| } | |
| if size > 0 && readBytes != size { | |
| _ = os.Remove(f.Name()) | |
| return nil, errs.NewErr(err, "CreateTempFile failed, incoming stream actual size= %d, expect = %d ", readBytes, size) | |
| } | |
| _, err = f.Seek(0, io.SeekStart) | |
| if err != nil { | |
| _ = os.Remove(f.Name()) | |
| return nil, errs.NewErr(err, "CreateTempFile failed, can't seek to 0 ") | |
| } | |
| return f, nil | |
| } | |
| // GetFileType get file type | |
| func GetFileType(filename string) int { | |
| ext := strings.ToLower(Ext(filename)) | |
| if SliceContains(conf.SlicesMap[conf.AudioTypes], ext) { | |
| return conf.AUDIO | |
| } | |
| if SliceContains(conf.SlicesMap[conf.VideoTypes], ext) { | |
| return conf.VIDEO | |
| } | |
| if SliceContains(conf.SlicesMap[conf.ImageTypes], ext) { | |
| return conf.IMAGE | |
| } | |
| if SliceContains(conf.SlicesMap[conf.TextTypes], ext) { | |
| return conf.TEXT | |
| } | |
| return conf.UNKNOWN | |
| } | |
| func GetObjType(filename string, isDir bool) int { | |
| if isDir { | |
| return conf.FOLDER | |
| } | |
| return GetFileType(filename) | |
| } | |
| var extraMimeTypes = map[string]string{ | |
| ".apk": "application/vnd.android.package-archive", | |
| } | |
| func GetMimeType(name string) string { | |
| ext := path.Ext(name) | |
| if m, ok := extraMimeTypes[ext]; ok { | |
| return m | |
| } | |
| m := mime.TypeByExtension(ext) | |
| if m != "" { | |
| return m | |
| } | |
| return "application/octet-stream" | |
| } | |
| const ( | |
| KB = 1 << (10 * (iota + 1)) | |
| MB | |
| GB | |
| TB | |
| ) | |