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  }