github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/internal/pcscommand/export.go (about) 1 package pcscommand 2 3 import ( 4 "container/list" 5 "fmt" 6 "os" 7 "path" 8 "strings" 9 "time" 10 11 "github.com/qjfoidnh/BaiduPCS-Go/baidupcs" 12 "github.com/qjfoidnh/BaiduPCS-Go/baidupcs/pcserror" 13 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/converter" 14 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/pcstime" 15 ) 16 17 type ( 18 etask struct { 19 *ListTask 20 path string 21 rootPath string 22 fd *baidupcs.FileDirectory 23 err pcserror.Error 24 } 25 26 // ExportOptions 导出可选项 27 ExportOptions struct { 28 RootPath string // 根路径 29 SavePath string // 输出路径 30 MaxRetry int 31 Recursive bool 32 LinkFormat bool 33 StdOut bool 34 } 35 ) 36 37 func (task *etask) handleExportTaskError(l *list.List, failedList *list.List) { 38 if task.err == nil { 39 return 40 } 41 42 // 不重试 43 switch task.err.GetError() { 44 case baidupcs.ErrGetRapidUploadInfoMD5NotFound, baidupcs.ErrGetRapidUploadInfoCrc32NotFound: 45 fmt.Printf("[%d] - [%s] 导出失败, 可能是服务器未刷新文件的md5, 请过一段时间再试一试\n", task.ID, task.path) 46 failedList.PushBack(task) 47 return 48 case baidupcs.ErrFileTooLarge: 49 fmt.Printf("[%d] - [%s] 导出失败, 文件大于20GB, 无法导出\n", task.ID, task.path) 50 failedList.PushBack(task) 51 return 52 } 53 54 // 未达到失败重试最大次数, 将任务推送到队列末尾 55 if task.retry < task.MaxRetry { 56 task.retry++ 57 fmt.Printf("[%d] - [%s] 导出错误, %s, 重试 %d/%d\n", task.ID, task.path, task.err, task.retry, task.MaxRetry) 58 l.PushBack(task) 59 time.Sleep(3 * time.Duration(task.retry) * time.Second) 60 } else { 61 fmt.Printf("[%d] - [%s] 导出错误, %s\n", task.ID, task.path, task.err) 62 failedList.PushBack(task) 63 } 64 } 65 66 func changeRootPath(dstRootPath, dstPath, srcRootPath string) string { 67 if srcRootPath == "" { 68 return dstPath 69 } 70 return path.Join(srcRootPath, strings.TrimPrefix(dstPath, dstRootPath)) 71 } 72 73 // GetExportFilename 获取导出路径 74 func GetExportFilename() string { 75 return "BaiduPCS-Go_export_" + pcstime.BeijingTimeOption("") + ".txt" 76 } 77 78 // RunExport 执行导出文件和目录 79 func RunExport(pcspaths []string, opt *ExportOptions) { 80 if opt == nil { 81 opt = &ExportOptions{} 82 } 83 84 if opt.SavePath == "" { 85 opt.SavePath = GetExportFilename() 86 } 87 88 pcspaths, err := matchPathByShellPattern(pcspaths...) 89 if err != nil { 90 fmt.Println(err) 91 return 92 } 93 saveFile, err := os.OpenFile(opt.SavePath, os.O_CREATE|os.O_RDWR|os.O_APPEND, 0644) 94 if err != nil { // 不可写 95 if !opt.StdOut { 96 fmt.Printf("%s\n", err) 97 return 98 } 99 } 100 defer saveFile.Close() 101 if !opt.StdOut { 102 fmt.Printf("导出的信息将保存在: %s\n", opt.SavePath) 103 } 104 105 var ( 106 au = GetActiveUser() 107 pcs = GetBaiduPCS() 108 l = list.New() 109 failedList = list.New() 110 writeErr error 111 id int 112 ) 113 114 for id = range pcspaths { 115 var rootPath string 116 if pcspaths[id] == au.Workdir { 117 rootPath = pcspaths[id] 118 } else { 119 rootPath = path.Dir(pcspaths[id]) 120 } 121 // 加入队列 122 l.PushBack(&etask{ 123 ListTask: &ListTask{ 124 ID: id, 125 MaxRetry: opt.MaxRetry, 126 }, 127 path: pcspaths[id], 128 rootPath: rootPath, 129 }) 130 } 131 132 for { 133 e := l.Front() 134 if e == nil { // 结束 135 break 136 } 137 138 l.Remove(e) // 载入任务后, 移除队列 139 140 task := e.Value.(*etask) 141 root := task.fd == nil 142 143 // 获取文件信息 144 if task.fd == nil { // 第一次初始化 145 fd, pcsError := pcs.FilesDirectoriesMeta(task.path) 146 if pcsError != nil { 147 task.err = pcsError 148 task.handleExportTaskError(l, failedList) 149 continue 150 } 151 task.fd = fd 152 } 153 154 if task.fd.Isdir { // 导出目录 155 if !root && !opt.Recursive { // 非递归 156 continue 157 } 158 159 fds, pcsError := pcs.FilesDirectoriesList(task.path, baidupcs.DefaultOrderOptions) 160 if pcsError != nil { 161 task.err = pcsError 162 task.handleExportTaskError(l, failedList) 163 continue 164 } 165 166 if len(fds) == 0 && !opt.StdOut { 167 _, writeErr = saveFile.Write(converter.ToBytes(fmt.Sprintf("BaiduPCS-Go mkdir \"%s\"\n", changeRootPath(task.rootPath, task.path, opt.RootPath)))) 168 if writeErr != nil { 169 fmt.Printf("写入文件失败: %s\n", writeErr) 170 return // 直接返回 171 } 172 fmt.Printf("[%d] - [%s] 导出成功\n", task.ID, task.path) 173 continue 174 } 175 176 // 加入队列 177 for _, fd := range fds { 178 // 加入队列 179 id++ 180 l.PushBack(&etask{ 181 ListTask: &ListTask{ 182 ID: id, 183 MaxRetry: opt.MaxRetry, 184 }, 185 path: fd.Path, 186 fd: fd, 187 rootPath: task.rootPath, 188 }) 189 } 190 continue 191 } 192 193 rinfo, pcsError := pcs.ExportByFileInfo(task.fd) 194 if pcsError != nil { 195 task.err = pcsError 196 task.handleExportTaskError(l, failedList) 197 continue 198 } 199 var outTemplate = fmt.Sprintf("BaiduPCS-Go rapidupload -length=%d -md5=%s -slicemd5=%s -crc32=%s \"%s\"\n", rinfo.ContentLength, rinfo.ContentMD5, rinfo.SliceMD5, rinfo.ContentCrc32, changeRootPath(task.rootPath, task.path, opt.RootPath)) 200 if opt.LinkFormat { 201 outTemplate = fmt.Sprintf("%s#%s#%d#%s\n", rinfo.ContentMD5, rinfo.SliceMD5, rinfo.ContentLength, path.Base(task.path)) 202 } 203 if opt.StdOut { 204 fmt.Print(outTemplate) 205 } else { 206 _, writeErr = saveFile.Write(converter.ToBytes(outTemplate)) 207 if writeErr != nil { 208 fmt.Printf("写入文件失败: %s\n", writeErr) 209 return // 直接返回 210 } 211 212 fmt.Printf("[%d] - [%s] 导出成功\n", task.ID, task.path) 213 } 214 } 215 if opt.StdOut { 216 os.Remove(opt.SavePath) 217 fmt.Println("导出完毕") 218 } 219 220 if failedList.Len() > 0 { 221 fmt.Printf("\n以下目录导出失败: \n") 222 fmt.Printf("%s\n", strings.Repeat("-", 100)) 223 for e := failedList.Front(); e != nil; e = e.Next() { 224 et := e.Value.(*etask) 225 fmt.Printf("[%d] %s\n", et.ID, et.path) 226 } 227 } 228 }