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 }