github.com/qjfoidnh/BaiduPCS-Go@v0.0.0-20231011165705-caa18a3765f3/baidupcs/prepare.go (about)

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