diff --git a/internal/functions/pandownload/utils.go b/internal/functions/pandownload/utils.go index 19be120..1d45764 100644 --- a/internal/functions/pandownload/utils.go +++ b/internal/functions/pandownload/utils.go @@ -15,6 +15,7 @@ package pandownload import ( "github.com/tickstep/aliyunpan-api/aliyunpan" + "github.com/tickstep/aliyunpan/internal/localfile" "os" ) @@ -26,7 +27,8 @@ func CheckFileValid(filePath string, fileInfo *aliyunpan.FileEntity) error { return nil } -// FileExist 检查文件是否存在, +// FileExist 检查文件是否存在 +// // 只有当文件存在, 文件大小不为0或断点续传文件不存在时, 才判断为存在 func FileExist(path string) bool { if info, err := os.Stat(path); err == nil { @@ -40,3 +42,23 @@ func FileExist(path string) bool { return false } + +// SymlinkFileExist 检查文件是否存在 +// +// 逻辑和 FileExist 一致,增加符号链接文件的支持 +func SymlinkFileExist(fullPath, rootPath string) bool { + originSaveRootSymlinkFile := localfile.NewSymlinkFile(rootPath) + suffixPath := localfile.GetSuffixPath(fullPath, rootPath) + savePathSymlinkFile, savePathFileInfo, err := localfile.RetrieveRealPathFromLogicSuffixPath(originSaveRootSymlinkFile, suffixPath) + if err != nil { + return false + } else { + if savePathFileInfo.Size() == 0 { + return false + } + if _, err = os.Stat(savePathSymlinkFile.RealPath + DownloadSuffix); err != nil { + return true + } + } + return false +} diff --git a/internal/localfile/symlink.go b/internal/localfile/symlink.go index b974e12..0575996 100644 --- a/internal/localfile/symlink.go +++ b/internal/localfile/symlink.go @@ -25,10 +25,7 @@ func (s *SymlinkFile) String() string { } func NewSymlinkFile(filePath string) SymlinkFile { - p := path.Clean(strings.ReplaceAll(filePath, "\\", "/")) - if p == "." { - p = "" - } + p := CleanPath(filePath) return SymlinkFile{ LogicPath: p, RealPath: p, @@ -37,6 +34,26 @@ func NewSymlinkFile(filePath string) SymlinkFile { type MyWalkFunc func(path SymlinkFile, info fs.FileInfo, err error) error +// CleanPath 规范化文件路径,分隔符全部转换成Unix文件分隔符"/"。同时清除后缀无用的"/"路径 +func CleanPath(p string) string { + if p == "" || p == "/" { + return p + } + + p = path.Clean(strings.ReplaceAll(p, "\\", "/")) + if p == "." { + p = "" + } + return p +} + +// GetSuffixPath 获取相对路径。即fullFilePath相对rootFilePath的相对路径 +func GetSuffixPath(fullFilePath, rootFilePath string) string { + rootFilePath = CleanPath(rootFilePath) + fullFilePath = CleanPath(fullFilePath) + return strings.TrimPrefix(strings.TrimPrefix(fullFilePath, rootFilePath), "/") +} + // RetrieveRealPath 递归调用找到软链接文件的真实文件对应的路径信息 func RetrieveRealPath(file SymlinkFile) (SymlinkFile, os.FileInfo, error) { info, err := os.Lstat(file.RealPath) @@ -62,11 +79,7 @@ func RetrieveRealPath(file SymlinkFile) (SymlinkFile, os.FileInfo, error) { // // 如果/Volumes/Downloads存在而后面部分不存在,则最终结果返回/Volumes/Downloads对应的文件信息,同时返回error func RetrieveRealPathFromLogicPath(logicFilePath string) (SymlinkFile, os.FileInfo, error) { - logicFilePath = strings.ReplaceAll(logicFilePath, "\\", "/") - logicFilePath = path.Clean(logicFilePath) - if logicFilePath == "." { - logicFilePath = "" - } + logicFilePath = CleanPath(logicFilePath) if logicFilePath == "/" { return RetrieveRealPath(NewSymlinkFile(logicFilePath)) } @@ -101,6 +114,45 @@ func RetrieveRealPathFromLogicPath(logicFilePath string) (SymlinkFile, os.FileIn return exitedSymlinkFile, exitedFileInfo, nil } +// RetrieveRealPathFromLogicSuffixPath 通过指定根目录和后缀逻辑路径,获取文件对应的真实路径信息 +// +// 整体功能和 RetrieveRealPathFromLogicPath 一致,将逻辑路径分成根路径和后缀路径两部分,并且根路径必须存在。 +func RetrieveRealPathFromLogicSuffixPath(rootFile SymlinkFile, logicSuffixPath string) (SymlinkFile, os.FileInfo, error) { + if rootFile.LogicPath == "" || rootFile.RealPath == "" { + return rootFile, nil, fmt.Errorf("root file path error") + } + rootFile.LogicPath = CleanPath(rootFile.LogicPath) + rootFile.RealPath = CleanPath(rootFile.RealPath) + rootFileInfo, e := os.Lstat(rootFile.RealPath) + if e != nil { + return rootFile, nil, e + } + + logicSuffixPath = CleanPath(logicSuffixPath) + pathParts := strings.Split(logicSuffixPath, "/") + exitedSymlinkFile := rootFile + exitedFileInfo := rootFileInfo + + sf := rootFile + var fi os.FileInfo + var err error + for _, p := range pathParts { + if p == "" { + continue + } + sf.LogicPath += "/" + p + sf.RealPath += "/" + p + sf, fi, err = RetrieveRealPath(sf) + if err != nil { + // may be permission deny or not existed + return exitedSymlinkFile, exitedFileInfo, err + } + exitedSymlinkFile = sf + exitedFileInfo = fi + } + return exitedSymlinkFile, exitedFileInfo, nil +} + // WalkAllFile 遍历本地文件,支持软链接(符号逻辑)文件(Linux & Windows & macOS) func WalkAllFile(file SymlinkFile, walkFn MyWalkFunc) error { file.LogicPath = path.Clean(strings.ReplaceAll(file.LogicPath, "\\", "/")) diff --git a/internal/localfile/symlink_test.go b/internal/localfile/symlink_test.go index 0bfbddb..0e4d1ce 100644 --- a/internal/localfile/symlink_test.go +++ b/internal/localfile/symlink_test.go @@ -50,3 +50,14 @@ func TestRetrieveRealPathFromLogicPath(t *testing.T) { } fmt.Println(sf) } + +func TestRetrieveRealPathFromLogicSuffixPath(t *testing.T) { + rootPath := NewSymlinkFile("/Volumes/Downloads/dev/测试同步盘/new_lks") + rootPath, _, _ = RetrieveRealPath(rootPath) + suffixPath := "test/未命名文件夹cmd/sync_drive_config.json" + sf, _, e := RetrieveRealPathFromLogicSuffixPath(rootPath, suffixPath) + if e != nil { + fmt.Println(e) + } + fmt.Println(sf) +}