github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/internal/pcscommand/upload.go (about) 1 package pcscommand 2 3 import ( 4 "fmt" 5 "github.com/qjfoidnh/BaiduPCS-Go/baidupcs" 6 "github.com/qjfoidnh/BaiduPCS-Go/internal/pcsconfig" 7 "github.com/qjfoidnh/BaiduPCS-Go/internal/pcsfunctions/pcsupload" 8 "github.com/qjfoidnh/BaiduPCS-Go/pcstable" 9 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil" 10 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/checksum" 11 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/converter" 12 "github.com/qjfoidnh/BaiduPCS-Go/pcsutil/taskframework" 13 "os" 14 "path" 15 "path/filepath" 16 "strings" 17 ) 18 19 const ( 20 // DefaultUploadMaxRetry 默认上传失败最大重试次数 21 DefaultUploadMaxRetry = 3 22 ) 23 24 type ( 25 // UploadOptions 上传可选项 26 UploadOptions struct { 27 Parallel int 28 MaxRetry int 29 Load int 30 NoRapidUpload bool 31 NoSplitFile bool // 禁用分片上传 32 Policy string // 同名文件处理策略 33 NoFilenameCheck bool // 禁用文件名合法性检查 34 } 35 ) 36 37 func uploadPrintFormat(load int) string { 38 if load <= 1 { 39 return pcsupload.DefaultPrintFormat 40 } 41 return "[%s] ↑ %s/%s %s/s in %s ...\n" 42 } 43 44 // RunRapidUpload 执行秒传文件, 前提是知道文件的大小, md5, 前256KB切片的 md5, crc32 45 func RunRapidUpload(targetPath, contentMD5, sliceMD5 string, length int64) { 46 dirname := path.Dir(targetPath) 47 err := matchPathByShellPatternOnce(&dirname) 48 if err != nil { 49 fmt.Printf("警告: %s, 获取网盘路径 %s 错误, %s\n", baidupcs.OperationRapidUpload, dirname, err) 50 } 51 err = GetBaiduPCS().APIRapidUpload(targetPath, contentMD5, sliceMD5, "", length) 52 if err != nil { 53 fmt.Printf("%s失败, 消息: %s\n", baidupcs.OperationRapidUpload, err) 54 return 55 } 56 57 fmt.Printf("%s成功, 保存到网盘路径: %s\n", baidupcs.OperationRapidUpload, targetPath) 58 return 59 } 60 61 // RunCreateSuperFile 执行分片上传—预上传文件及合并分片文件 62 func RunCreateSuperFile(policy string, targetPath string, blockList ...string) { 63 err := matchPathByShellPatternOnce(&targetPath) 64 if err != nil { 65 fmt.Printf("警告: %s, 获取网盘路径 %s 错误, %s\n", baidupcs.OperationUploadCreateSuperFile, targetPath, err) 66 } 67 68 err = GetBaiduPCS().UploadCreateSuperFile(policy, true, targetPath, blockList...) 69 if err != nil { 70 fmt.Printf("%s失败, 消息: %s\n", baidupcs.OperationUploadCreateSuperFile, err) 71 return 72 } 73 74 fmt.Printf("%s成功, 保存到网盘路径: %s\n", baidupcs.OperationUploadCreateSuperFile, targetPath) 75 return 76 } 77 78 // RunUpload 执行文件上传 79 func RunUpload(localPaths []string, savePath string, opt *UploadOptions) { 80 if opt == nil { 81 opt = &UploadOptions{} 82 } 83 84 // 检测opt 85 if opt.Parallel <= 0 { 86 opt.Parallel = pcsconfig.Config.MaxUploadParallel 87 } 88 89 opt.NoFilenameCheck = pcsconfig.Config.IgnoreIllegal 90 91 if opt.MaxRetry < 0 { 92 opt.MaxRetry = DefaultUploadMaxRetry 93 } 94 95 if opt.Load <=0 { 96 opt.Load = pcsconfig.Config.MaxUploadLoad 97 } 98 99 if opt.Policy!="fail" && opt.Policy!="newcopy" && opt.Policy!="overwrite" && opt.Policy!="skip" && opt.Policy!="rsync" { 100 opt.Policy = pcsconfig.Config.UPolicy 101 } 102 103 err := matchPathByShellPatternOnce(&savePath) 104 if err != nil { 105 fmt.Printf("警告: 上传文件, 获取网盘路径 %s 错误, %s\n", savePath, err) 106 } 107 108 switch len(localPaths) { 109 case 0: 110 fmt.Printf("本地路径为空\n") 111 return 112 } 113 114 // 打开上传状态 115 uploadDatabase, err := pcsupload.NewUploadingDatabase() 116 if err != nil { 117 fmt.Printf("打开上传未完成数据库错误: %s\n", err) 118 return 119 } 120 defer uploadDatabase.Close() 121 122 var ( 123 pcs = GetBaiduPCS() 124 // 使用 task framework 125 executor = &taskframework.TaskExecutor{ 126 IsFailedDeque: true, // 失败统计 127 } 128 subSavePath string 129 // 统计 130 statistic = &pcsupload.UploadStatistic{} 131 ) 132 fmt.Print("\n") 133 fmt.Printf("[0] 提示: 当前上传单个文件最大并发量为: %d, 最大同时上传文件数为: %d\n", opt.Parallel, opt.Load) 134 135 statistic.StartTimer() // 开始计时 136 137 LoadCount := 0 138 139 for k := range localPaths { 140 walkedFiles, err := pcsutil.WalkDir(localPaths[k], "") 141 if err != nil { 142 fmt.Printf("警告: 遍历错误: %s\n", err) 143 continue 144 } 145 146 for k3 := range walkedFiles { 147 var localPathDir string 148 // 针对 windows 的目录处理 149 if os.PathSeparator == '\\' { 150 walkedFiles[k3] = pcsutil.ConvertToUnixPathSeparator(walkedFiles[k3]) 151 localPathDir = pcsutil.ConvertToUnixPathSeparator(filepath.Dir(localPaths[k])) 152 } else { 153 localPathDir = filepath.Dir(localPaths[k]) 154 } 155 156 // 避免去除文件名开头的"." 157 if localPathDir == "." { 158 localPathDir = "" 159 } 160 if len(localPaths) == 1 && len(walkedFiles) == 1 { 161 opt.Load = 1 162 } 163 subSavePath = strings.TrimPrefix(walkedFiles[k3], localPathDir) 164 if !opt.NoFilenameCheck && !pcsutil.ChPathLegal(walkedFiles[k3]) { 165 fmt.Printf("[0] %s 文件路径含有非法字符,已跳过!\n", walkedFiles[k3]) 166 continue 167 } 168 LoadCount++ 169 info := executor.Append(&pcsupload.UploadTaskUnit{ 170 LocalFileChecksum: checksum.NewLocalFileChecksum(walkedFiles[k3], int(baidupcs.SliceMD5Size)), 171 SavePath: path.Clean(savePath + baidupcs.PathSeparator + subSavePath), 172 PCS: pcs, 173 UploadingDatabase: uploadDatabase, 174 Parallel: opt.Parallel, 175 PrintFormat: uploadPrintFormat(opt.Load), 176 NoRapidUpload: opt.NoRapidUpload, 177 NoSplitFile: opt.NoSplitFile, 178 UploadStatistic: statistic, 179 Policy: opt.Policy, 180 }, opt.MaxRetry) 181 if LoadCount >= opt.Load { 182 LoadCount = opt.Load 183 } 184 fmt.Printf("[%s] 加入上传队列: %s\n", info.Id(), walkedFiles[k3]) 185 } 186 } 187 188 // 没有添加任何任务 189 if executor.Count() == 0 { 190 fmt.Printf("未检测到上传的文件.\n") 191 return 192 } 193 194 // 设置上传文件并发数 195 executor.SetParallel(LoadCount) 196 // 执行上传任务 197 executor.Execute() 198 199 fmt.Printf("\n") 200 fmt.Printf("上传结束, 时间: %s, 总大小: %s\n", statistic.Elapsed()/1e6*1e6, converter.ConvertFileSize(statistic.TotalSize())) 201 202 // 输出上传失败的文件列表 203 failedList := executor.FailedDeque() 204 if failedList.Size() != 0 { 205 fmt.Printf("以下文件上传失败: \n") 206 tb := pcstable.NewTable(os.Stdout) 207 for e := failedList.Shift(); e != nil; e = failedList.Shift() { 208 item := e.(*taskframework.TaskInfoItem) 209 tb.Append([]string{item.Info.Id(), item.Unit.(*pcsupload.UploadTaskUnit).LocalFileChecksum.Path}) 210 } 211 tb.Render() 212 } 213 }