github.com/fzfile/BaiduPCS-Go@v0.0.0-20200606205115-4408961cf336/baidupcs/file_directory.go (about)

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