2022-08-19 17:27:54 +08:00
|
|
|
|
package command
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
"github.com/tickstep/aliyunpan-api/aliyunpan"
|
|
|
|
|
"github.com/tickstep/aliyunpan/cmder"
|
|
|
|
|
"github.com/tickstep/aliyunpan/internal/config"
|
2022-10-09 19:09:17 +08:00
|
|
|
|
"github.com/tickstep/library-go/converter"
|
2022-08-19 17:27:54 +08:00
|
|
|
|
"github.com/urfave/cli"
|
|
|
|
|
"path"
|
|
|
|
|
"strings"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func CmdTree() cli.Command {
|
|
|
|
|
return cli.Command{
|
|
|
|
|
Name: "tree",
|
|
|
|
|
Usage: "列出目录的树形图",
|
|
|
|
|
UsageText: cmder.App().Name + " tree <目录>",
|
|
|
|
|
Description: `
|
|
|
|
|
列出指定目录内的文件和目录, 并以树形图的方式呈现
|
|
|
|
|
|
|
|
|
|
示例:
|
|
|
|
|
|
|
|
|
|
列出 当前工作目录 内的文件和目录的树形图
|
|
|
|
|
aliyunpan tree
|
|
|
|
|
|
|
|
|
|
列出 /我的资源 内的文件和目录的树形图
|
|
|
|
|
aliyunpan tree /我的资源
|
|
|
|
|
|
2022-12-06 08:41:07 +08:00
|
|
|
|
列出 /我的资源 内的文件和目录的树形图,使用通配符
|
|
|
|
|
aliyunpan tree /我的*
|
|
|
|
|
|
2022-08-31 11:06:57 +08:00
|
|
|
|
列出 /我的资源 内的文件和目录的树形图,并且显示文件对应的完整绝对路径
|
|
|
|
|
aliyunpan tree -fp /我的资源
|
2023-02-23 14:43:02 +08:00
|
|
|
|
|
|
|
|
|
列出 /我的资源 内的文件和目录的树形图,并且显示文件对应的文件大小
|
|
|
|
|
aliyunpan tree -fs /我的资源
|
|
|
|
|
|
|
|
|
|
列出 /我的资源 内的文件和目录的树形图,过滤大于等于 10kb 并且小于等于 10mb 的文件,同时显示文件对应的文件大小
|
|
|
|
|
aliyunpan tree -fs -minSize=1kb -maxSize=10mb /我的资源
|
2022-08-19 17:27:54 +08:00
|
|
|
|
`,
|
|
|
|
|
Category: "阿里云盘",
|
2022-12-10 14:07:28 +08:00
|
|
|
|
Before: ReloadConfigFunc,
|
2022-08-19 17:27:54 +08:00
|
|
|
|
Action: func(c *cli.Context) error {
|
|
|
|
|
if config.Config.ActiveUser() == nil {
|
|
|
|
|
fmt.Println("未登录账号")
|
|
|
|
|
return nil
|
|
|
|
|
}
|
2023-02-23 14:43:02 +08:00
|
|
|
|
minSize := int64(0)
|
|
|
|
|
if c.IsSet("minSize") {
|
|
|
|
|
if s, e := converter.ParseFileSizeStr(c.String("minSize")); e == nil {
|
|
|
|
|
minSize = s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
maxSize := int64(0)
|
|
|
|
|
if c.IsSet("maxSize") {
|
|
|
|
|
if s, e := converter.ParseFileSizeStr(c.String("maxSize")); e == nil {
|
|
|
|
|
maxSize = s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
RunTree(parseDriveId(c), c.Args().Get(0), c.Bool("fp"), c.Bool("fs"), minSize, maxSize)
|
2022-08-19 17:27:54 +08:00
|
|
|
|
return nil
|
|
|
|
|
},
|
|
|
|
|
Flags: []cli.Flag{
|
|
|
|
|
cli.StringFlag{
|
|
|
|
|
Name: "driveId",
|
|
|
|
|
Usage: "网盘ID",
|
|
|
|
|
Value: "",
|
|
|
|
|
},
|
2022-08-31 11:06:57 +08:00
|
|
|
|
cli.BoolFlag{
|
|
|
|
|
Name: "fp",
|
2023-02-23 14:43:02 +08:00
|
|
|
|
Usage: "full path, 树形图显示文件的完整路径",
|
|
|
|
|
},
|
|
|
|
|
cli.BoolFlag{
|
|
|
|
|
Name: "fs",
|
|
|
|
|
Usage: "file size, 树形图显示文件的文件大小",
|
|
|
|
|
},
|
|
|
|
|
cli.StringFlag{
|
|
|
|
|
Name: "minSize",
|
|
|
|
|
Usage: "min size, 过滤大于等于指定大小的文件,例如:100mb",
|
|
|
|
|
},
|
|
|
|
|
cli.StringFlag{
|
|
|
|
|
Name: "maxSize",
|
|
|
|
|
Usage: "max size, 过滤小于等于指定大小的文件,例如:1gb",
|
2022-08-31 11:06:57 +08:00
|
|
|
|
},
|
2022-08-19 17:27:54 +08:00
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
indentPrefix = "│ "
|
|
|
|
|
pathPrefix = "├──"
|
|
|
|
|
lastFilePrefix = "└──"
|
|
|
|
|
)
|
|
|
|
|
|
2022-08-31 10:36:33 +08:00
|
|
|
|
type (
|
|
|
|
|
treeStatistic struct {
|
|
|
|
|
CountOfDir int64
|
|
|
|
|
CountOfFile int64
|
2022-10-09 19:09:17 +08:00
|
|
|
|
SizeOfFile int64
|
2022-08-31 10:36:33 +08:00
|
|
|
|
}
|
2023-02-23 14:43:02 +08:00
|
|
|
|
|
|
|
|
|
treeConfig struct {
|
|
|
|
|
showFullPath bool
|
|
|
|
|
showFileSize bool
|
|
|
|
|
minFileSize int64
|
|
|
|
|
maxFileSize int64
|
|
|
|
|
}
|
2022-08-31 10:36:33 +08:00
|
|
|
|
)
|
|
|
|
|
|
2023-02-23 14:43:02 +08:00
|
|
|
|
func getTree(driveId, pathStr string, depth int, statistic *treeStatistic, setting *treeConfig) {
|
2022-08-19 17:27:54 +08:00
|
|
|
|
activeUser := config.Config.ActiveUser()
|
|
|
|
|
pathStr = activeUser.PathJoin(driveId, pathStr)
|
|
|
|
|
pathStr = path.Clean(pathStr)
|
|
|
|
|
|
2024-03-02 17:38:21 +08:00
|
|
|
|
// 获取目标路径文件信息
|
|
|
|
|
targetPathInfo, err := activeUser.PanClient().OpenapiPanClient().FileInfoByPath(driveId, pathStr)
|
2022-12-05 20:19:52 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
2022-08-19 17:27:54 +08:00
|
|
|
|
return
|
|
|
|
|
}
|
2022-12-05 20:19:52 +08:00
|
|
|
|
|
2024-03-02 17:38:21 +08:00
|
|
|
|
// 适配通配符路径获取目标文件信息(弃用,容易触发风控)
|
|
|
|
|
//files, err := matchPathByShellPattern(driveId, pathStr)
|
|
|
|
|
//if err != nil {
|
|
|
|
|
// fmt.Println(err)
|
|
|
|
|
// return
|
|
|
|
|
//}
|
|
|
|
|
//var targetPathInfo *aliyunpan.FileEntity
|
|
|
|
|
//if len(files) == 1 {
|
|
|
|
|
// targetPathInfo = files[0]
|
|
|
|
|
//} else {
|
|
|
|
|
// for _, f := range files {
|
|
|
|
|
// if f.IsFolder() {
|
|
|
|
|
// targetPathInfo = f
|
|
|
|
|
// break
|
|
|
|
|
// }
|
|
|
|
|
// }
|
|
|
|
|
//}
|
|
|
|
|
|
2022-12-05 20:19:52 +08:00
|
|
|
|
if targetPathInfo == nil {
|
|
|
|
|
fmt.Println("路径不存在")
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if depth == 0 {
|
|
|
|
|
fmt.Printf("%s\n", targetPathInfo.Path)
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-19 17:27:54 +08:00
|
|
|
|
fileList := aliyunpan.FileList{}
|
|
|
|
|
fileListParam := &aliyunpan.FileListParam{}
|
|
|
|
|
fileListParam.ParentFileId = targetPathInfo.FileId
|
|
|
|
|
fileListParam.DriveId = driveId
|
|
|
|
|
fileListParam.OrderBy = aliyunpan.FileOrderByName
|
|
|
|
|
fileListParam.OrderDirection = aliyunpan.FileOrderDirectionAsc
|
|
|
|
|
if targetPathInfo.IsFolder() {
|
2024-03-02 17:38:21 +08:00
|
|
|
|
fileResult, err := activeUser.PanClient().OpenapiPanClient().FileListGetAll(fileListParam, 500)
|
2022-08-19 17:27:54 +08:00
|
|
|
|
if err != nil {
|
|
|
|
|
fmt.Println(err)
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
fileList = append(fileList, fileResult...)
|
|
|
|
|
} else {
|
|
|
|
|
fileList = append(fileList, targetPathInfo)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var (
|
|
|
|
|
prefix = pathPrefix
|
|
|
|
|
fN = len(fileList)
|
|
|
|
|
indentPrefixStr = strings.Repeat(indentPrefix, depth)
|
|
|
|
|
)
|
|
|
|
|
for i, file := range fileList {
|
|
|
|
|
if file.IsFolder() {
|
2022-08-31 10:36:33 +08:00
|
|
|
|
statistic.CountOfDir += 1
|
2023-02-23 14:43:02 +08:00
|
|
|
|
if setting.showFullPath {
|
2022-12-05 20:19:52 +08:00
|
|
|
|
fmt.Printf("%v%v %v/ -> %s\n", indentPrefixStr, pathPrefix, file.FileName, targetPathInfo.Path+"/"+file.FileName)
|
2022-08-31 11:06:57 +08:00
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf("%v%v %v/\n", indentPrefixStr, pathPrefix, file.FileName)
|
|
|
|
|
}
|
2024-03-02 17:38:21 +08:00
|
|
|
|
getTree(driveId, targetPathInfo.Path+"/"+file.FileName, depth+1, statistic, setting)
|
2022-08-19 17:27:54 +08:00
|
|
|
|
continue
|
|
|
|
|
}
|
2023-02-23 14:43:02 +08:00
|
|
|
|
|
|
|
|
|
// filter file size
|
|
|
|
|
if setting.minFileSize > 0 {
|
|
|
|
|
if file.FileSize < setting.minFileSize {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if setting.maxFileSize > 0 {
|
|
|
|
|
if file.FileSize > setting.maxFileSize {
|
|
|
|
|
continue
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-31 10:36:33 +08:00
|
|
|
|
statistic.CountOfFile += 1
|
2022-10-09 19:09:17 +08:00
|
|
|
|
statistic.SizeOfFile += file.FileSize
|
2022-08-19 17:27:54 +08:00
|
|
|
|
|
|
|
|
|
if i+1 == fN {
|
|
|
|
|
prefix = lastFilePrefix
|
|
|
|
|
}
|
|
|
|
|
|
2023-02-23 14:43:02 +08:00
|
|
|
|
// 文件大小
|
|
|
|
|
fileName := &strings.Builder{}
|
|
|
|
|
if setting.showFileSize {
|
|
|
|
|
fmt.Fprintf(fileName, "%s (%s)", file.FileName, converter.ConvertFileSize(file.FileSize, 2))
|
2022-08-31 11:06:57 +08:00
|
|
|
|
} else {
|
2023-02-23 14:43:02 +08:00
|
|
|
|
fmt.Fprintf(fileName, "%s", file.FileName)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 文件完整路径
|
|
|
|
|
if setting.showFullPath {
|
|
|
|
|
fmt.Printf("%v%v %v -> %s\n", indentPrefixStr, prefix, fileName.String(), targetPathInfo.Path+"/"+file.FileName)
|
|
|
|
|
} else {
|
|
|
|
|
fmt.Printf("%v%v %v\n", indentPrefixStr, prefix, fileName.String())
|
2022-08-31 11:06:57 +08:00
|
|
|
|
}
|
2022-08-19 17:27:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// RunTree 列出树形图
|
2023-02-23 14:43:02 +08:00
|
|
|
|
func RunTree(driveId, pathStr string, showFullPath, showFileSize bool, minSize, maxSize int64) {
|
2022-08-19 17:27:54 +08:00
|
|
|
|
activeUser := config.Config.ActiveUser()
|
2024-03-02 17:38:21 +08:00
|
|
|
|
activeUser.PanClient().OpenapiPanClient().ClearCache()
|
|
|
|
|
activeUser.PanClient().OpenapiPanClient().EnableCache()
|
|
|
|
|
defer activeUser.PanClient().OpenapiPanClient().DisableCache()
|
2022-08-19 17:27:54 +08:00
|
|
|
|
pathStr = activeUser.PathJoin(driveId, pathStr)
|
2022-08-31 10:36:33 +08:00
|
|
|
|
statistic := &treeStatistic{
|
|
|
|
|
CountOfDir: 0,
|
|
|
|
|
CountOfFile: 0,
|
2022-10-09 19:09:17 +08:00
|
|
|
|
SizeOfFile: 0,
|
2022-08-31 10:36:33 +08:00
|
|
|
|
}
|
2023-02-23 14:43:02 +08:00
|
|
|
|
setting := &treeConfig{
|
|
|
|
|
showFullPath: showFullPath,
|
|
|
|
|
showFileSize: showFileSize,
|
|
|
|
|
minFileSize: minSize,
|
|
|
|
|
maxFileSize: maxSize,
|
|
|
|
|
}
|
|
|
|
|
getTree(driveId, pathStr, 0, statistic, setting)
|
2022-10-09 19:09:17 +08:00
|
|
|
|
fmt.Printf("\n%d 个文件夹, %d 个文件, %s 总大小\n", statistic.CountOfDir, statistic.CountOfFile, converter.ConvertFileSize(statistic.SizeOfFile, 2))
|
2022-08-19 17:27:54 +08:00
|
|
|
|
}
|