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