github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/baidupcs/file_directory.go (about)

     1  package baidupcs
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"github.com/olekukonko/tablewriter"
     7  	"github.com/qjfoidnh/BaiduPCS-Go/baidupcs/pcserror"
     8  	"github.com/qjfoidnh/BaiduPCS-Go/pcstable"
     9  	"github.com/qjfoidnh/BaiduPCS-Go/pcsutil/converter"
    10  	"github.com/qjfoidnh/BaiduPCS-Go/pcsutil/pcstime"
    11  	"path/filepath"
    12  	"strconv"
    13  	"strings"
    14  	"unsafe"
    15  )
    16  
    17  type (
    18  	// OrderBy 排序字段
    19  	OrderBy string
    20  	// Order 升序降序
    21  	Order string
    22  )
    23  
    24  const (
    25  	// OrderByName 根据文件名排序
    26  	OrderByName OrderBy = "name"
    27  	// OrderByTime 根据时间排序
    28  	OrderByTime OrderBy = "time"
    29  	// OrderBySize 根据大小排序, 注意目录无大小
    30  	OrderBySize OrderBy = "size"
    31  	// OrderAsc 升序
    32  	OrderAsc Order = "asc"
    33  	// OrderDesc 降序
    34  	OrderDesc Order = "desc"
    35  )
    36  
    37  type (
    38  	// HandleFileDirectoryFunc 处理文件或目录的元信息, 返回值控制是否退出递归
    39  	HandleFileDirectoryFunc func(depth int, fdPath string, fd *FileDirectory, pcsError pcserror.Error) bool
    40  
    41  	// FileDirectory 文件或目录的元信息
    42  	FileDirectory struct {
    43  		FsID     int64  // fs_id
    44  		AppID    int64  // app_id
    45  		Path     string // 路径
    46  		Filename string // 文件名 或 目录名
    47  		Ctime    int64  // 创建日期
    48  		Mtime    int64  // 修改日期
    49  		MD5      string // md5 值
    50  		BlockListJSON
    51  		Size        int64 // 文件大小 (目录为0)
    52  		Isdir       bool  // 是否为目录
    53  		Ifhassubdir bool  // 是否含有子目录 (只对目录有效)
    54  		PreBase   string  // 真正的base目录
    55  
    56  		Parent   *FileDirectory    // 父目录信息
    57  		Children FileDirectoryList // 子目录信息
    58  	}
    59  
    60  	// FileDirectoryList FileDirectory 的 指针数组
    61  	FileDirectoryList []*FileDirectory
    62  
    63  	// fdJSON 用于解析远程JSON数据
    64  	fdJSON struct {
    65  		FsID     int64  `json:"fs_id"` // fs_id
    66  		AppID    int64  `json:"app_id"`
    67  		Path     string `json:"path"`            // 路径
    68  		Filename string `json:"server_filename"` // 文件名 或 目录名
    69  		Ctime    int64  `json:"ctime"`           // 创建日期
    70  		Mtime    int64  `json:"mtime"`           // 修改日期
    71  		MD5      string `json:"md5"`             // md5 值
    72  		BlockListJSON
    73  		Size           int64 `json:"size"` // 文件大小 (目录为0)
    74  		IsdirInt       int8  `json:"isdir"`
    75  		IfhassubdirInt int8  `json:"ifhassubdir"`
    76  
    77  		// 对齐
    78  		_ *fdJSON
    79  		_ []*fdJSON
    80  	}
    81  
    82  	fdData struct {
    83  		*pcserror.PCSErrInfo
    84  		List FileDirectoryList
    85  	}
    86  
    87  	fdDataJSONExport struct {
    88  		*pcserror.PCSErrInfo
    89  		List []*fdJSON `json:"list"`
    90  	}
    91  
    92  	// OrderOptions 列文件/目录可选项
    93  	OrderOptions struct {
    94  		By    OrderBy
    95  		Order Order
    96  	}
    97  )
    98  
    99  var (
   100  	// DefaultOrderOptions 默认的排序
   101  	DefaultOrderOptions = &OrderOptions{
   102  		By:    OrderByName,
   103  		Order: OrderAsc,
   104  	}
   105  
   106  	defaultOrderOptionsStr = fmt.Sprint(DefaultOrderOptions)
   107  )
   108  
   109  // FilesDirectoriesMeta 获取单个文件/目录的元信息
   110  func (pcs *BaiduPCS) FilesDirectoriesMeta(path string) (data *FileDirectory, pcsError pcserror.Error) {
   111  	if path == "" {
   112  		path = PathSeparator
   113  	}
   114  
   115  	fds, err := pcs.FilesDirectoriesBatchMeta(path)
   116  	if err != nil {
   117  		return nil, err
   118  	}
   119  
   120  	// 返回了多条元信息
   121  	if len(fds) != 1 {
   122  		return nil, &pcserror.PCSErrInfo{
   123  			Operation: OperationFilesDirectoriesMeta,
   124  			ErrType:   pcserror.ErrTypeOthers,
   125  			Err:       errors.New("未知返回数据"),
   126  		}
   127  	}
   128  	return fds[0], nil
   129  }
   130  
   131  // FilesDirectoriesBatchMeta 获取多个文件/目录的元信息
   132  func (pcs *BaiduPCS) FilesDirectoriesBatchMeta(paths ...string) (data FileDirectoryList, pcsError pcserror.Error) {
   133  	dataReadCloser, pcsError := pcs.PrepareFilesDirectoriesBatchMeta(paths...)
   134  	if pcsError != nil {
   135  		return nil, pcsError
   136  	}
   137  
   138  	defer dataReadCloser.Close()
   139  
   140  	errInfo := pcserror.NewPCSErrorInfo(OperationFilesDirectoriesMeta)
   141  	// 服务器返回数据进行处理
   142  	jsonData := fdData{
   143  		PCSErrInfo: errInfo,
   144  	}
   145  
   146  	pcsError = pcserror.HandleJSONParse(OperationFilesDirectoriesMeta, dataReadCloser, (*fdDataJSONExport)(unsafe.Pointer(&jsonData)))
   147  	if pcsError != nil {
   148  		return
   149  	}
   150  
   151  	// 修复MD5
   152  	jsonData.List.fixMD5()
   153  
   154  	data = jsonData.List
   155  	return
   156  }
   157  
   158  // FilesDirectoriesList 获取目录下的文件和目录列表
   159  func (pcs *BaiduPCS) FilesDirectoriesList(path string, options *OrderOptions) (data FileDirectoryList, pcsError pcserror.Error) {
   160  	dataReadCloser, pcsError := pcs.PrepareFilesDirectoriesList(path, options)
   161  	if pcsError != nil {
   162  		return nil, pcsError
   163  	}
   164  
   165  	defer dataReadCloser.Close()
   166  
   167  	jsonData := fdData{
   168  		PCSErrInfo: pcserror.NewPCSErrorInfo(OperationFilesDirectoriesList),
   169  	}
   170  
   171  	pcsError = pcserror.HandleJSONParse(OperationFilesDirectoriesList, dataReadCloser, (*fdDataJSONExport)(unsafe.Pointer(&jsonData)))
   172  	if pcsError != nil {
   173  		return nil, pcsError
   174  	}
   175  
   176  	// 修复MD5
   177  	jsonData.List.fixMD5()
   178  
   179  	data = jsonData.List
   180  	return
   181  }
   182  
   183  // Search 按文件名搜索文件, 不支持查找目录
   184  func (pcs *BaiduPCS) Search(targetPath, keyword string, recursive bool) (fdl FileDirectoryList, pcsError pcserror.Error) {
   185  	if targetPath == "" {
   186  		targetPath = PathSeparator
   187  	}
   188  
   189  	dataReadCloser, pcsError := pcs.PrepareSearch(targetPath, keyword, recursive)
   190  	if pcsError != nil {
   191  		return nil, pcsError
   192  	}
   193  
   194  	defer dataReadCloser.Close()
   195  
   196  	errInfo := pcserror.NewPCSErrorInfo(OperationSearch)
   197  	jsonData := fdData{
   198  		PCSErrInfo: errInfo,
   199  	}
   200  
   201  	pcsError = pcserror.HandleJSONParse(OperationSearch, dataReadCloser, (*fdDataJSONExport)(unsafe.Pointer(&jsonData)))
   202  	if pcsError != nil {
   203  		return
   204  	}
   205  
   206  	// 修复MD5
   207  	jsonData.List.fixMD5()
   208  
   209  	fdl = jsonData.List
   210  	return
   211  }
   212  
   213  func (pcs *BaiduPCS) recurseList(path string, depth int, options *OrderOptions, prebase string, handleFileDirectoryFunc HandleFileDirectoryFunc) (fdl FileDirectoryList, ok bool) {
   214  	fdl, pcsError := pcs.FilesDirectoriesList(path, options)
   215  	if pcsError != nil {
   216  		ok := handleFileDirectoryFunc(depth, path, nil, pcsError) // 传递错误
   217  		return nil, ok
   218  	}
   219  
   220  	for k := range fdl {
   221  		fdl[k].PreBase = prebase
   222  		ok = handleFileDirectoryFunc(depth+1, fdl[k].Path, fdl[k], nil)
   223  		if !ok {
   224  			return
   225  		}
   226  
   227  		if !fdl[k].Isdir {
   228  			continue
   229  		}
   230  
   231  		fdl[k].Children, ok = pcs.recurseList(fdl[k].Path, depth+1, options, filepath.Join(prebase, filepath.Base(fdl[k].Path)), handleFileDirectoryFunc)
   232  		if !ok {
   233  			return
   234  		}
   235  	}
   236  
   237  	return fdl, true
   238  }
   239  
   240  // FilesDirectoriesRecurseList 递归获取目录下的文件和目录列表
   241  func (pcs *BaiduPCS) FilesDirectoriesRecurseList(path string, options *OrderOptions, handleFileDirectoryFunc HandleFileDirectoryFunc) (data FileDirectoryList) {
   242  	fd, pcsError := pcs.FilesDirectoriesMeta(path)
   243  	if pcsError != nil {
   244  		handleFileDirectoryFunc(0, path, nil, pcsError) // 传递错误
   245  		return nil
   246  	}
   247  
   248  	if !fd.Isdir { // 不是一个目录
   249  		handleFileDirectoryFunc(0, path, fd, nil)
   250  		return FileDirectoryList{fd}
   251  	} else {
   252  		handleFileDirectoryFunc(0, path, fd, nil)
   253  	}
   254  
   255  	data, _ = pcs.recurseList(path, 0, options, filepath.Base(path), handleFileDirectoryFunc)
   256  	return data
   257  }
   258  
   259  // fixMD5 尝试修复MD5字段
   260  // 服务器返回的MD5字段不一定正确了, 即是BlockList只有一个md5
   261  // MD5字段使用BlockList中的md5
   262  func (f *FileDirectory) fixMD5() {
   263  	if len(f.BlockList) != 1 {
   264  		return
   265  	}
   266  	f.MD5 = f.BlockList[0]
   267  }
   268  
   269  func (f *FileDirectory) String() string {
   270  	builder := &strings.Builder{}
   271  	tb := pcstable.NewTable(builder)
   272  	tb.SetColumnAlignment([]int{tablewriter.ALIGN_LEFT, tablewriter.ALIGN_LEFT})
   273  
   274  	if f.Isdir {
   275  		tb.AppendBulk([][]string{
   276  			[]string{"类型", "目录"},
   277  			[]string{"目录路径", f.Path},
   278  			[]string{"目录名称", f.Filename},
   279  		})
   280  	} else {
   281  		var md5info string
   282  		if len(f.BlockList) > 1 {
   283  			md5info = "md5 (可能不正确)"
   284  		} else {
   285  			md5info = "md5 (截图请打码)"
   286  		}
   287  		tb.AppendBulk([][]string{
   288  			[]string{"类型", "文件"},
   289  			[]string{"文件路径", f.Path},
   290  			[]string{"文件名称", f.Filename},
   291  			[]string{"文件大小", strconv.FormatInt(f.Size, 10) + ", " + converter.ConvertFileSize(f.Size)},
   292  			[]string{md5info, f.MD5},
   293  		})
   294  	}
   295  
   296  	tb.Append([]string{"app_id", strconv.FormatInt(f.AppID, 10)})
   297  	tb.Append([]string{"fs_id", strconv.FormatInt(f.FsID, 10)})
   298  	tb.AppendBulk([][]string{
   299  		[]string{"创建日期", pcstime.FormatTime(f.Ctime)},
   300  		[]string{"修改日期", pcstime.FormatTime(f.Mtime)},
   301  	})
   302  
   303  	if f.Ifhassubdir {
   304  		tb.Append([]string{"是否含有子目录", "true"})
   305  	}
   306  
   307  	tb.Render()
   308  	return builder.String()
   309  }
   310  
   311  func (fl FileDirectoryList) fixMD5() {
   312  	for _, v := range fl {
   313  		v.fixMD5()
   314  		v.MD5 = DecryptMD5(v.MD5)
   315  	}
   316  }
   317  
   318  // TotalSize 获取目录下文件的总大小
   319  func (fl FileDirectoryList) TotalSize() int64 {
   320  	var size int64
   321  	for k := range fl {
   322  		if fl[k] == nil {
   323  			continue
   324  		}
   325  
   326  		size += fl[k].Size
   327  
   328  		// 递归获取
   329  		if fl[k].Children != nil {
   330  			size += fl[k].Children.TotalSize()
   331  		}
   332  	}
   333  	return size
   334  }
   335  
   336  // Count 获取文件总数和目录总数
   337  func (fl FileDirectoryList) Count() (fileN, directoryN int64) {
   338  	for k := range fl {
   339  		if fl[k] == nil {
   340  			continue
   341  		}
   342  
   343  		if fl[k].Isdir {
   344  			directoryN++
   345  		} else {
   346  			fileN++
   347  		}
   348  
   349  		// 递归获取
   350  		if fl[k].Children != nil {
   351  			fN, dN := fl[k].Children.Count()
   352  			fileN += fN
   353  			directoryN += dN
   354  		}
   355  	}
   356  	return
   357  }
   358  
   359  // AllFilePaths 返回所有的网盘路径, 包括子目录
   360  func (fl FileDirectoryList) AllFilePaths() (pcspaths []string) {
   361  	fN, dN := fl.Count()
   362  	pcspaths = make([]string, 0, fN+dN)
   363  	for k := range fl {
   364  		if fl[k] == nil {
   365  			continue
   366  		}
   367  
   368  		pcspaths = append(pcspaths, fl[k].Path)
   369  
   370  		if fl[k].Children != nil {
   371  			pcspaths = append(pcspaths, fl[k].Children.AllFilePaths()...)
   372  		}
   373  	}
   374  	return
   375  }