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 }