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  }