github.com/fzfile/BaiduPCS-Go@v0.0.0-20200606205115-4408961cf336/baidupcs/prepare.go (about)

     1  package baidupcs
     2  
     3  import (
     4  	"bytes"
     5  	"fmt"
     6  	"github.com/fzfile/BaiduPCS-Go/baidupcs/netdisksign"
     7  	"github.com/fzfile/BaiduPCS-Go/baidupcs/pcserror"
     8  	"github.com/fzfile/BaiduPCS-Go/pcsutil/converter"
     9  	"github.com/fzfile/BaiduPCS-Go/requester/multipartreader"
    10  	"github.com/fzfile/baidu-tools/tieba"
    11  	"github.com/json-iterator/go"
    12  	"io"
    13  	"net/http"
    14  	"net/url"
    15  	"strconv"
    16  	"strings"
    17  	"unsafe"
    18  )
    19  
    20  type (
    21  	reqType int
    22  )
    23  
    24  const (
    25  	reqTypePCS = iota
    26  	reqTypePan
    27  )
    28  
    29  func handleRespClose(resp *http.Response) error {
    30  	if resp != nil {
    31  		return resp.Body.Close()
    32  	}
    33  	return nil
    34  }
    35  
    36  func handleRespStatusError(opreation string, resp *http.Response) pcserror.Error {
    37  	errInfo := pcserror.NewPCSErrorInfo(opreation)
    38  	// http 响应错误处理
    39  	switch resp.StatusCode / 100 {
    40  	case 4, 5:
    41  		resp.Body.Close()
    42  		errInfo.SetNetError(fmt.Errorf("http 响应错误, %s", resp.Status))
    43  		return errInfo
    44  	}
    45  
    46  	return nil
    47  }
    48  
    49  func (pcs *BaiduPCS) sendReqReturnResp(rt reqType, op, method, urlStr string, post interface{}, header map[string]string) (resp *http.Response, pcsError pcserror.Error) {
    50  	if header == nil {
    51  		header = map[string]string{}
    52  	}
    53  
    54  	var (
    55  		_, uaok = header["User-Agent"]
    56  	)
    57  
    58  	if !uaok {
    59  		switch rt {
    60  		case reqTypePCS:
    61  			header["User-Agent"] = pcs.pcsUA
    62  		case reqTypePan:
    63  			header["User-Agent"] = pcs.panUA
    64  		}
    65  	}
    66  
    67  	resp, err := pcs.client.Req(method, urlStr, post, header)
    68  	if err != nil {
    69  		handleRespClose(resp)
    70  		switch rt {
    71  		case reqTypePCS:
    72  			return nil, &pcserror.PCSErrInfo{
    73  				Operation: op,
    74  				ErrType:   pcserror.ErrTypeNetError,
    75  				Err:       err,
    76  			}
    77  		case reqTypePan:
    78  			return nil, &pcserror.PanErrorInfo{
    79  				Operation: op,
    80  				ErrType:   pcserror.ErrTypeNetError,
    81  				Err:       err,
    82  			}
    83  		}
    84  		panic("unreachable")
    85  	}
    86  	return resp, nil
    87  }
    88  
    89  func (pcs *BaiduPCS) sendReqReturnReadCloser(rt reqType, op, method, urlStr string, post interface{}, header map[string]string) (readCloser io.ReadCloser, pcsError pcserror.Error) {
    90  	resp, pcsError := pcs.sendReqReturnResp(rt, op, method, urlStr, post, header)
    91  	if pcsError != nil {
    92  		return
    93  	}
    94  	return resp.Body, nil
    95  }
    96  
    97  // PrepareUK 获取用户 UK, 只返回服务器响应数据和错误信息
    98  func (pcs *BaiduPCS) PrepareUK() (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
    99  	pcs.lazyInit()
   100  
   101  	query := url.Values{}
   102  	query.Set("need_selfinfo", "1")
   103  
   104  	panURL := &url.URL{
   105  		Scheme:   "https",
   106  		Host:     PanBaiduCom,
   107  		Path:     "api/user/getinfo",
   108  		RawQuery: query.Encode(),
   109  	}
   110  
   111  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationGetUK, http.MethodGet, panURL.String(), nil, nil)
   112  	return
   113  }
   114  
   115  // PrepareQuotaInfo 获取当前用户空间配额信息, 只返回服务器响应数据和错误信息
   116  func (pcs *BaiduPCS) PrepareQuotaInfo() (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   117  	pcs.lazyInit()
   118  	pcsURL := pcs.generatePCSURL("quota", "info")
   119  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationQuotaInfo, pcsURL)
   120  
   121  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationQuotaInfo, http.MethodGet, pcsURL.String(), nil, nil)
   122  	return
   123  }
   124  
   125  // PrepareFilesDirectoriesBatchMeta 获取多个文件/目录的元信息, 只返回服务器响应数据和错误信息
   126  func (pcs *BaiduPCS) PrepareFilesDirectoriesBatchMeta(paths ...string) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   127  	pcs.lazyInit()
   128  	sendData, err := (&PathsListJSON{}).JSON(paths...)
   129  	if err != nil {
   130  		panic(OperationFilesDirectoriesMeta + ", json 数据构造失败, " + err.Error())
   131  	}
   132  
   133  	pcsURL := pcs.generatePCSURL("file", "meta")
   134  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationFilesDirectoriesMeta, pcsURL)
   135  
   136  	// 表单上传
   137  	mr := multipartreader.NewMultipartReader()
   138  	mr.AddFormFeild("param", bytes.NewReader(sendData))
   139  	mr.CloseMultipart()
   140  
   141  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationFilesDirectoriesMeta, http.MethodPost, pcsURL.String(), mr, nil)
   142  	return
   143  }
   144  
   145  // PrepareFilesDirectoriesList 获取目录下的文件和目录列表, 只返回服务器响应数据和错误信息
   146  func (pcs *BaiduPCS) PrepareFilesDirectoriesList(path string, options *OrderOptions) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   147  	pcs.lazyInit()
   148  	if options == nil {
   149  		options = DefaultOrderOptions
   150  	}
   151  	if path == "" {
   152  		path = PathSeparator
   153  	}
   154  
   155  	pcsURL := pcs.generatePCSURL("file", "list", map[string]string{
   156  		"path":  path,
   157  		"by":    *(*string)(unsafe.Pointer(&options.By)),
   158  		"order": *(*string)(unsafe.Pointer(&options.Order)),
   159  		"limit": "0-2147483647",
   160  	})
   161  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationFilesDirectoriesList, pcsURL)
   162  
   163  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationFilesDirectoriesList, http.MethodGet, pcsURL.String(), nil, nil)
   164  	return
   165  }
   166  
   167  // PrepareSearch 按文件名搜索文件, 只返回服务器响应数据和错误信息
   168  func (pcs *BaiduPCS) PrepareSearch(targetPath, keyword string, recursive bool) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   169  	pcs.lazyInit()
   170  	var re string
   171  	if recursive {
   172  		re = "1"
   173  	} else {
   174  		re = "0"
   175  	}
   176  	pcsURL := pcs.generatePCSURL("file", "search", map[string]string{
   177  		"path": targetPath,
   178  		"wd":   keyword,
   179  		"re":   re,
   180  	})
   181  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationSearch, pcsURL)
   182  
   183  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationSearch, http.MethodGet, pcsURL.String(), nil, nil)
   184  	return
   185  }
   186  
   187  // PrepareRemove 批量删除文件/目录, 只返回服务器响应数据和错误信息
   188  func (pcs *BaiduPCS) PrepareRemove(paths ...string) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   189  	pcs.lazyInit()
   190  	sendData, err := (&PathsListJSON{}).JSON(paths...)
   191  	if err != nil {
   192  		panic(OperationMove + ", json 数据构造失败, " + err.Error())
   193  	}
   194  
   195  	pcsURL := pcs.generatePCSURL("file", "delete")
   196  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationRemove, pcsURL)
   197  
   198  	// 表单上传
   199  	mr := multipartreader.NewMultipartReader()
   200  	mr.AddFormFeild("param", bytes.NewReader(sendData))
   201  	mr.CloseMultipart()
   202  
   203  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationRemove, http.MethodPost, pcsURL.String(), mr, nil)
   204  	return
   205  }
   206  
   207  // PrepareMkdir 创建目录, 只返回服务器响应数据和错误信息
   208  func (pcs *BaiduPCS) PrepareMkdir(pcspath string) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   209  	pcs.lazyInit()
   210  	pcsURL := pcs.generatePCSURL("file", "mkdir", map[string]string{
   211  		"path": pcspath,
   212  	})
   213  	baiduPCSVerbose.Infof("%s URL: %s", OperationMkdir, pcsURL)
   214  
   215  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationMkdir, http.MethodPost, pcsURL.String(), nil, nil)
   216  	return
   217  }
   218  
   219  func (pcs *BaiduPCS) prepareCpMvOp(op string, cpmvJSON ...*CpMvJSON) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   220  	pcs.lazyInit()
   221  	var method string
   222  	switch op {
   223  	case OperationCopy:
   224  		method = "copy"
   225  	case OperationMove, OperationRename:
   226  		method = "move"
   227  	default:
   228  		panic("Unknown opreation: " + op)
   229  	}
   230  
   231  	sendData, err := (&CpMvListJSON{
   232  		List: cpmvJSON,
   233  	}).JSON()
   234  	if err != nil {
   235  		//json 数据生成失败
   236  		panic(err)
   237  	}
   238  
   239  	pcsURL := pcs.generatePCSURL("file", method)
   240  	baiduPCSVerbose.Infof("%s URL: %s\n", op, pcsURL)
   241  
   242  	// 表单上传
   243  	mr := multipartreader.NewMultipartReader()
   244  	mr.AddFormFeild("param", bytes.NewReader(sendData))
   245  	mr.CloseMultipart()
   246  
   247  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, op, http.MethodPost, pcsURL.String(), mr, nil)
   248  	return
   249  }
   250  
   251  // PrepareRename 重命名文件/目录, 只返回服务器响应数据和错误信息
   252  func (pcs *BaiduPCS) PrepareRename(from, to string) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   253  	return pcs.prepareCpMvOp(OperationRename, &CpMvJSON{
   254  		From: from,
   255  		To:   to,
   256  	})
   257  }
   258  
   259  // PrepareCopy 批量拷贝文件/目录, 只返回服务器响应数据和错误信息
   260  func (pcs *BaiduPCS) PrepareCopy(cpmvJSON ...*CpMvJSON) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   261  	return pcs.prepareCpMvOp(OperationCopy, cpmvJSON...)
   262  }
   263  
   264  // PrepareMove 批量移动文件/目录, 只返回服务器响应数据和错误信息
   265  func (pcs *BaiduPCS) PrepareMove(cpmvJSON ...*CpMvJSON) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   266  	return pcs.prepareCpMvOp(OperationMove, cpmvJSON...)
   267  }
   268  
   269  // prepareRapidUpload 秒传文件, 不进行文件夹检查
   270  func (pcs *BaiduPCS) prepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   271  	pcs.lazyInit()
   272  	pcsURL := pcs.generatePCSURL("file", "rapidupload", map[string]string{
   273  		"path":           targetPath,                    // 上传文件的全路径名
   274  		"content-length": strconv.FormatInt(length, 10), // 待秒传的文件长度
   275  		"content-md5":    contentMD5,                    // 待秒传的文件的MD5
   276  		"slice-md5":      sliceMD5,                      // 待秒传的文件前256kb的MD5
   277  		"content-crc32":  crc32,                         // 待秒传文件CRC32
   278  		"ondup":          "overwrite",                   // overwrite: 表示覆盖同名文件; newcopy: 表示生成文件副本并进行重命名,命名规则为“文件名_日期.后缀”
   279  	})
   280  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationRapidUpload, pcsURL)
   281  
   282  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationRapidUpload, http.MethodGet, pcsURL.String(), nil, nil)
   283  	return
   284  }
   285  
   286  // PrepareRapidUpload 秒传文件, 只返回服务器响应数据和错误信息
   287  func (pcs *BaiduPCS) PrepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   288  	pcs.lazyInit()
   289  	pcsError = pcs.checkIsdir(OperationRapidUpload, targetPath)
   290  	if pcsError != nil {
   291  		return nil, pcsError
   292  	}
   293  
   294  	return pcs.prepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32, length)
   295  }
   296  
   297  // PrepareLocateDownload 获取下载链接, 只返回服务器响应数据和错误信息
   298  func (pcs *BaiduPCS) PrepareLocateDownload(pcspath string) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   299  	pcs.lazyInit()
   300  	bduss := pcs.GetBDUSS()
   301  	// 检测uid
   302  	if pcs.uid == 0 {
   303  		t, err := tieba.NewUserInfoByBDUSS(bduss)
   304  		if err != nil {
   305  			return nil, &pcserror.PCSErrInfo{
   306  				Operation: OperationLocateDownload,
   307  				ErrType:   pcserror.ErrTypeNetError,
   308  				Err:       err,
   309  			}
   310  		}
   311  		pcs.uid = t.Baidu.UID
   312  	}
   313  
   314  	ns := netdisksign.NewLocateDownloadSign(pcs.uid, bduss)
   315  	pcsURL := &url.URL{
   316  		Scheme: GetHTTPScheme(pcs.isHTTPS),
   317  		Host:   PCSBaiduCom,
   318  		Path:   "/rest/2.0/pcs/file",
   319  		RawQuery: (url.Values{
   320  			"app_id": []string{PanAppID},
   321  			"method": []string{"locatedownload"},
   322  			"path":   []string{pcspath},
   323  			"ver":    []string{"2"},
   324  		}).Encode() + "&" + ns.URLParam(),
   325  	}
   326  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationLocateDownload, pcsURL)
   327  
   328  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationLocateDownload, http.MethodGet, pcsURL.String(), nil, pcs.getPanUAHeader())
   329  	return
   330  }
   331  
   332  // PrepareLocatePanAPIDownload 从百度网盘首页获取下载链接, 只返回服务器响应数据和错误信息
   333  func (pcs *BaiduPCS) PrepareLocatePanAPIDownload(fidList ...int64) (dataReadCloser io.ReadCloser, panError pcserror.Error) {
   334  	pcs.lazyInit()
   335  	// 初始化
   336  	var (
   337  		sign, err = pcs.ph.CacheSignature()
   338  	)
   339  	if err != nil {
   340  		return nil, &pcserror.PanErrorInfo{
   341  			Operation: OperationLocatePanAPIDownload,
   342  			ErrType:   pcserror.ErrTypeOthers,
   343  			Err:       err,
   344  		}
   345  	}
   346  
   347  	panURL := pcs.generatePanURL("download", nil)
   348  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationLocatePanAPIDownload, panURL)
   349  
   350  	dataReadCloser, panError = pcs.sendReqReturnReadCloser(reqTypePan, OperationLocatePanAPIDownload, http.MethodPost, panURL.String(), map[string]string{
   351  		"sign":      sign.Sign(),
   352  		"timestamp": sign.Timestamp(),
   353  		"fidlist":   mergeInt64List(fidList...),
   354  	}, map[string]string{
   355  		"Content-Type": "application/x-www-form-urlencoded",
   356  	})
   357  	return
   358  }
   359  
   360  // PrepareUpload 上传单个文件, 只返回服务器响应数据和错误信息
   361  func (pcs *BaiduPCS) PrepareUpload(targetPath string, uploadFunc UploadFunc) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   362  	pcs.lazyInit()
   363  	pcsError = pcs.checkIsdir(OperationUpload, targetPath)
   364  	if pcsError != nil {
   365  		return nil, pcsError
   366  	}
   367  
   368  	pcsURL := pcs.generatePCSURL("file", "upload", map[string]string{
   369  		"path":  targetPath,
   370  		"ondup": "overwrite",
   371  	})
   372  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationUpload, pcsURL)
   373  
   374  	resp, err := uploadFunc(pcsURL.String(), pcs.client.Jar)
   375  	if err != nil {
   376  		handleRespClose(resp)
   377  		return nil, &pcserror.PCSErrInfo{
   378  			Operation: OperationUpload,
   379  			ErrType:   pcserror.ErrTypeNetError,
   380  			Err:       err,
   381  		}
   382  	}
   383  
   384  	pcsError = handleRespStatusError(OperationUpload, resp)
   385  	if pcsError != nil {
   386  		return
   387  	}
   388  
   389  	return resp.Body, nil
   390  }
   391  
   392  // PrepareUploadTmpFile 分片上传—文件分片及上传, 只返回服务器响应数据和错误信息
   393  func (pcs *BaiduPCS) PrepareUploadTmpFile(uploadFunc UploadFunc) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   394  	pcs.lazyInit()
   395  	pcsURL := pcs.generatePCSURL("file", "upload", map[string]string{
   396  		"type": "tmpfile",
   397  	})
   398  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationUploadTmpFile, pcsURL)
   399  
   400  	resp, err := uploadFunc(pcsURL.String(), pcs.client.Jar)
   401  	if err != nil {
   402  		handleRespClose(resp)
   403  		return nil, &pcserror.PCSErrInfo{
   404  			Operation: OperationUploadTmpFile,
   405  			ErrType:   pcserror.ErrTypeNetError,
   406  			Err:       err,
   407  		}
   408  	}
   409  
   410  	pcsError = handleRespStatusError(OperationUploadTmpFile, resp)
   411  	if pcsError != nil {
   412  		return
   413  	}
   414  
   415  	return resp.Body, nil
   416  }
   417  
   418  // PrepareUploadCreateSuperFile 分片上传—合并分片文件, 只返回服务器响应数据和错误信息
   419  func (pcs *BaiduPCS) PrepareUploadCreateSuperFile(checkDir bool, targetPath string, blockList ...string) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   420  	pcs.lazyInit()
   421  
   422  	if checkDir {
   423  		// 检查是否为目录
   424  		pcsError = pcs.checkIsdir(OperationUploadCreateSuperFile, targetPath)
   425  		if pcsError != nil {
   426  			return nil, pcsError
   427  		}
   428  	}
   429  
   430  	bl := BlockListJSON{
   431  		BlockList: blockList,
   432  	}
   433  
   434  	sendData, err := jsoniter.Marshal(&bl)
   435  	if err != nil {
   436  		panic(err)
   437  	}
   438  
   439  	pcsURL := pcs.generatePCSURL("file", "createsuperfile", map[string]string{
   440  		"path":  targetPath,
   441  		"ondup": "overwrite",
   442  	})
   443  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationUploadCreateSuperFile, pcsURL)
   444  
   445  	// 表单上传
   446  	mr := multipartreader.NewMultipartReader()
   447  	mr.AddFormFeild("param", bytes.NewReader(sendData))
   448  	mr.CloseMultipart()
   449  
   450  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationUploadCreateSuperFile, http.MethodPost, pcsURL.String(), mr, nil)
   451  	return
   452  }
   453  
   454  // PrepareUploadPrecreate 分片上传—Precreate, 只返回服务器响应数据和错误信息
   455  func (pcs *BaiduPCS) PrepareUploadPrecreate(targetPath, contentMD5, sliceMD5, crc32 string, size int64, bolckList ...string) (dataReadCloser io.ReadCloser, panError pcserror.Error) {
   456  	pcs.lazyInit()
   457  	panURL := &url.URL{
   458  		Scheme: "https",
   459  		Host:   PanBaiduCom,
   460  		Path:   "api/precreate",
   461  	}
   462  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationUploadPrecreate, panURL)
   463  
   464  	dataReadCloser, panError = pcs.sendReqReturnReadCloser(reqTypePan, OperationUploadPrecreate, http.MethodPost, panURL.String(), map[string]string{
   465  		"path":         targetPath,
   466  		"size":         strconv.FormatInt(size, 10),
   467  		"isdir":        "0",
   468  		"block_list":   mergeStringList(bolckList...),
   469  		"autoinit":     "1",
   470  		"content-md5":  contentMD5,
   471  		"slice-md5":    sliceMD5,
   472  		"contentCrc32": crc32,
   473  		"rtype":        "2",
   474  	}, map[string]string{
   475  		"Content-Type": "application/x-www-form-urlencoded",
   476  	})
   477  	return
   478  }
   479  
   480  // PrepareUploadSuperfile2 另一个上传接口
   481  func (pcs *BaiduPCS) PrepareUploadSuperfile2(uploadid, targetPath string, partseq int, partOffset int64, uploadFunc UploadFunc) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   482  	pcs.lazyInit()
   483  	pcsURL := pcs.generatePCSURL("superfile2", "upload", map[string]string{
   484  		"type":       "tmpfile",
   485  		"path":       targetPath,
   486  		"partseq":    strconv.Itoa(partseq),
   487  		"partoffset": strconv.FormatInt(partOffset, 10),
   488  		"uploadid":   uploadid,
   489  		"vip":        "1",
   490  	})
   491  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationUploadSuperfile2, pcsURL)
   492  
   493  	resp, err := uploadFunc(pcsURL.String(), pcs.client.Jar)
   494  	if err != nil {
   495  		handleRespClose(resp)
   496  		return nil, &pcserror.PCSErrInfo{
   497  			Operation: OperationUploadSuperfile2,
   498  			ErrType:   pcserror.ErrTypeNetError,
   499  			Err:       err,
   500  		}
   501  	}
   502  
   503  	pcsError = handleRespStatusError(OperationUpload, resp)
   504  	if pcsError != nil {
   505  		return
   506  	}
   507  	return resp.Body, nil
   508  }
   509  
   510  // PrepareCloudDlAddTask 添加离线下载任务, 只返回服务器响应数据和错误信息
   511  func (pcs *BaiduPCS) PrepareCloudDlAddTask(sourceURL, savePath string) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   512  	pcs.lazyInit()
   513  	pcsURL2 := pcs.generatePCSURL2("services/cloud_dl", "add_task", map[string]string{
   514  		"save_path":  savePath,
   515  		"source_url": sourceURL,
   516  		"timeout":    "2147483647",
   517  	})
   518  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationCloudDlAddTask, pcsURL2)
   519  
   520  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationCloudDlAddTask, http.MethodPost, pcsURL2.String(), nil, nil)
   521  	return
   522  }
   523  
   524  // PrepareCloudDlQueryTask 精确查询离线下载任务, 只返回服务器响应数据和错误信息,
   525  // taskids 例子: 12123,234234,2344, 用逗号隔开多个 task_id
   526  func (pcs *BaiduPCS) PrepareCloudDlQueryTask(taskIDs string) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   527  	pcs.lazyInit()
   528  	pcsURL2 := pcs.generatePCSURL2("services/cloud_dl", "query_task", map[string]string{
   529  		"op_type": "1",
   530  	})
   531  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationCloudDlQueryTask, pcsURL2)
   532  
   533  	// 表单上传
   534  	mr := multipartreader.NewMultipartReader()
   535  	mr.AddFormFeild("task_ids", strings.NewReader(taskIDs))
   536  	mr.CloseMultipart()
   537  
   538  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationCloudDlQueryTask, http.MethodPost, pcsURL2.String(), mr, nil)
   539  	return
   540  }
   541  
   542  // PrepareCloudDlListTask 查询离线下载任务列表, 只返回服务器响应数据和错误信息
   543  func (pcs *BaiduPCS) PrepareCloudDlListTask() (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   544  	pcs.lazyInit()
   545  	pcsURL2 := pcs.generatePCSURL2("services/cloud_dl", "list_task", map[string]string{
   546  		"need_task_info": "1",
   547  		"status":         "255",
   548  		"start":          "0",
   549  		"limit":          "1000",
   550  	})
   551  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationCloudDlListTask, pcsURL2)
   552  
   553  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationCloudDlListTask, http.MethodPost, pcsURL2.String(), nil, nil)
   554  	return
   555  }
   556  
   557  func (pcs *BaiduPCS) prepareCloudDlCDTask(opreation, method string, taskID int64) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   558  	pcs.lazyInit()
   559  	pcsURL2 := pcs.generatePCSURL2("services/cloud_dl", method, map[string]string{
   560  		"task_id": strconv.FormatInt(taskID, 10),
   561  	})
   562  	baiduPCSVerbose.Infof("%s URL: %s\n", opreation, pcsURL2)
   563  
   564  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, opreation, http.MethodPost, pcsURL2.String(), nil, nil)
   565  	return
   566  }
   567  
   568  // PrepareCloudDlCancelTask 取消离线下载任务, 只返回服务器响应数据和错误信息
   569  func (pcs *BaiduPCS) PrepareCloudDlCancelTask(taskID int64) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   570  	return pcs.prepareCloudDlCDTask(OperationCloudDlCancelTask, "cancel_task", taskID)
   571  }
   572  
   573  // PrepareCloudDlDeleteTask 取消离线下载任务, 只返回服务器响应数据和错误信息
   574  func (pcs *BaiduPCS) PrepareCloudDlDeleteTask(taskID int64) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   575  	return pcs.prepareCloudDlCDTask(OperationCloudDlDeleteTask, "delete_task", taskID)
   576  }
   577  
   578  // PrepareCloudDlClearTask 清空离线下载任务记录, 只返回服务器响应数据和错误信息
   579  func (pcs *BaiduPCS) PrepareCloudDlClearTask() (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   580  	pcs.lazyInit()
   581  	pcsURL2 := pcs.generatePCSURL2("services/cloud_dl", "clear_task")
   582  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationCloudDlClearTask, pcsURL2)
   583  
   584  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationCloudDlClearTask, http.MethodPost, pcsURL2.String(), nil, nil)
   585  	return
   586  }
   587  
   588  // PrepareSharePSet 私密分享文件, 只返回服务器响应数据和错误信息
   589  func (pcs *BaiduPCS) PrepareSharePSet(paths []string, period int) (dataReadCloser io.ReadCloser, panError pcserror.Error) {
   590  	pcs.lazyInit()
   591  	panURL := &url.URL{
   592  		Scheme: "https",
   593  		Host:   PanBaiduCom,
   594  		Path:   "share/pset",
   595  	}
   596  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationShareSet, panURL)
   597  
   598  	dataReadCloser, panError = pcs.sendReqReturnReadCloser(reqTypePan, OperationShareSet, http.MethodPost, panURL.String(), map[string]string{
   599  		"path_list":    mergeStringList(paths...),
   600  		"schannel":     "0",
   601  		"channel_list": "[]",
   602  		"period":       strconv.Itoa(period),
   603  	}, map[string]string{
   604  		"Content-Type": "application/x-www-form-urlencoded",
   605  	})
   606  	return
   607  }
   608  
   609  // PrepareShareCancel 取消分享, 只返回服务器响应数据和错误信息
   610  func (pcs *BaiduPCS) PrepareShareCancel(shareIDs []int64) (dataReadCloser io.ReadCloser, panError pcserror.Error) {
   611  	pcs.lazyInit()
   612  	panURL := &url.URL{
   613  		Scheme: "https",
   614  		Host:   PanBaiduCom,
   615  		Path:   "share/cancel",
   616  	}
   617  
   618  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationShareCancel, panURL)
   619  
   620  	ss := converter.SliceInt64ToString(shareIDs)
   621  	dataReadCloser, panError = pcs.sendReqReturnReadCloser(reqTypePan, OperationShareCancel, http.MethodPost, panURL.String(), map[string]string{
   622  		"shareid_list": "[" + strings.Join(ss, ",") + "]",
   623  	}, map[string]string{
   624  		"Content-Type": "application/x-www-form-urlencoded",
   625  	})
   626  	return
   627  }
   628  
   629  // PrepareShareList 列出分享列表, 只返回服务器响应数据和错误信息
   630  func (pcs *BaiduPCS) PrepareShareList(page int) (dataReadCloser io.ReadCloser, panError pcserror.Error) {
   631  	pcs.lazyInit()
   632  
   633  	query := url.Values{}
   634  	query.Set("page", strconv.Itoa(page))
   635  	query.Set("desc", "1")
   636  	query.Set("order", "time")
   637  
   638  	panURL := &url.URL{
   639  		Scheme:   "https",
   640  		Host:     PanBaiduCom,
   641  		Path:     "share/record",
   642  		RawQuery: query.Encode(),
   643  	}
   644  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationShareList, panURL)
   645  
   646  	dataReadCloser, panError = pcs.sendReqReturnReadCloser(reqTypePan, OperationShareList, http.MethodGet, panURL.String(), nil, nil)
   647  	return
   648  }
   649  
   650  // PrepareShareSURLInfo 获取分享的详细信息, 包含密码, 只返回服务器响应数据和错误信息
   651  func (pcs *BaiduPCS) PrepareShareSURLInfo(shareID int64) (dataReadCloser io.ReadCloser, panError pcserror.Error) {
   652  	pcs.lazyInit()
   653  
   654  	query := url.Values{}
   655  	query.Set("shareid", strconv.FormatInt(shareID, 10))
   656  	query.Set("sign", converter.ToString(netdisksign.ShareSURLInfoSign(shareID)))
   657  
   658  	panURL := &url.URL{
   659  		Scheme:   "https",
   660  		Host:     PanBaiduCom,
   661  		Path:     "share/surlinfoinrecord",
   662  		RawQuery: query.Encode(),
   663  	}
   664  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationShareSURLInfo, panURL)
   665  
   666  	dataReadCloser, panError = pcs.sendReqReturnReadCloser(reqTypePan, OperationShareSURLInfo, http.MethodGet, panURL.String(), nil, nil)
   667  	return
   668  }
   669  
   670  // PrepareRecycleList 列出回收站文件列表, 只返回服务器响应数据和错误信息
   671  func (pcs *BaiduPCS) PrepareRecycleList(page int) (dataReadCloser io.ReadCloser, panError pcserror.Error) {
   672  	pcs.lazyInit()
   673  
   674  	panURL := pcs.generatePanURL("recycle/list", map[string]string{
   675  		"num":  "100",
   676  		"page": strconv.Itoa(page),
   677  	})
   678  
   679  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationRecycleList, panURL)
   680  
   681  	dataReadCloser, panError = pcs.sendReqReturnReadCloser(reqTypePan, OperationRecycleList, http.MethodGet, panURL.String(), nil, nil)
   682  	return
   683  }
   684  
   685  // PrepareRecycleRestore 还原回收站文件或目录, 只返回服务器响应数据和错误信息
   686  func (pcs *BaiduPCS) PrepareRecycleRestore(fidList ...int64) (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   687  	pcs.lazyInit()
   688  
   689  	pcsURL := pcs.generatePCSURL("file", "restore")
   690  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationRecycleRestore, pcsURL)
   691  
   692  	fsIDList := make([]*FsIDJSON, 0, len(fidList))
   693  	for k := range fidList {
   694  		fsIDList = append(fsIDList, &FsIDJSON{
   695  			FsID: fidList[k],
   696  		})
   697  	}
   698  	fsIDListJSON := FsIDListJSON{
   699  		List: fsIDList,
   700  	}
   701  
   702  	sendData, err := jsoniter.Marshal(&fsIDListJSON)
   703  	if err != nil {
   704  		panic(err)
   705  	}
   706  
   707  	// 表单上传
   708  	mr := multipartreader.NewMultipartReader()
   709  	mr.AddFormFeild("param", bytes.NewReader(sendData))
   710  	mr.CloseMultipart()
   711  
   712  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationRecycleRestore, http.MethodPost, pcsURL.String(), mr, nil)
   713  	return
   714  }
   715  
   716  // PrepareRecycleDelete 删除回收站文件或目录, 只返回服务器响应数据和错误信息
   717  func (pcs *BaiduPCS) PrepareRecycleDelete(fidList ...int64) (dataReadCloser io.ReadCloser, panError pcserror.Error) {
   718  	pcs.lazyInit()
   719  
   720  	panURL := pcs.generatePanURL("recycle/delete", nil)
   721  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationRecycleDelete, panURL)
   722  
   723  	dataReadCloser, panError = pcs.sendReqReturnReadCloser(reqTypePan, OperationRecycleDelete, http.MethodPost, panURL.String(), map[string]string{
   724  		"fidlist": mergeInt64List(fidList...),
   725  	}, map[string]string{
   726  		"Content-Type": "application/x-www-form-urlencoded",
   727  	})
   728  	return
   729  }
   730  
   731  // PrepareRecycleClear 清空回收站, 只返回服务器响应数据和错误信息
   732  func (pcs *BaiduPCS) PrepareRecycleClear() (dataReadCloser io.ReadCloser, pcsError pcserror.Error) {
   733  	pcs.lazyInit()
   734  
   735  	pcsURL := pcs.generatePCSURL("file", "delete", map[string]string{
   736  		"type": "recycle",
   737  	})
   738  
   739  	baiduPCSVerbose.Infof("%s URL: %s\n", OperationRecycleClear, pcsURL)
   740  
   741  	dataReadCloser, pcsError = pcs.sendReqReturnReadCloser(reqTypePCS, OperationRecycleClear, http.MethodGet, pcsURL.String(), nil, nil)
   742  	return
   743  }