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

     1  package baidupcs
     2  
     3  import (
     4  	"errors"
     5  	"math/rand"
     6  	"net/http"
     7  	"path"
     8  	"strings"
     9  	"time"
    10  
    11  	"github.com/qjfoidnh/BaiduPCS-Go/baidupcs/pcserror"
    12  	"github.com/qjfoidnh/BaiduPCS-Go/pcsutil/converter"
    13  )
    14  
    15  const (
    16  	// MaxUploadBlockSize 最大上传的文件分片大小
    17  	MaxUploadBlockSize = 2 * converter.GB
    18  	// MinUploadBlockSize 最小的上传的文件分片大小
    19  	MinUploadBlockSize = 4 * converter.MB
    20  	// MaxRapidUploadSize 秒传文件支持的最大文件大小
    21  	MaxRapidUploadSize = 20 * converter.GB
    22  	// RecommendUploadBlockSize 推荐的上传的文件分片大小
    23  	RecommendUploadBlockSize = 1 * converter.GB
    24  	// SliceMD5Size 计算 slice-md5 所需的长度
    25  	SliceMD5Size = 256 * converter.KB
    26  	// EmptyContentMD5 空串的md5
    27  	EmptyContentMD5 = "d41d8cd98f00b204e9800998ecf8427e"
    28  )
    29  
    30  var (
    31  	// ErrUploadMD5NotFound 未找到md5
    32  	ErrUploadMD5NotFound = errors.New("unknown response data, md5 not found")
    33  	// ErrUploadSavePathFound 未找到保存路径
    34  	ErrUploadSavePathFound = errors.New("unknown response data, file saved path not found")
    35  	// ErrUploadSeqNotMatch 服务器返回的上传队列不匹配
    36  	ErrUploadSeqNotMatch = errors.New("服务器返回的上传队列不匹配")
    37  	// ErrUploadMD5Unknown 服务器无匹配文件/秒传未生效
    38  	ErrUploadMD5Unknown = errors.New("服务器无匹配文件/秒传未生效")
    39  	// ErrUploadFileExists 文件或目录已存在
    40  	ErrUploadFileExists = errors.New("文件已存在")
    41  )
    42  
    43  type (
    44  	// UploadFunc 上传文件处理函数
    45  	UploadFunc func(uploadURL string, jar http.CookieJar) (resp *http.Response, err error)
    46  
    47  	// RapidUploadInfo 文件秒传信息
    48  	RapidUploadInfo struct {
    49  		Filename      string
    50  		ContentLength int64
    51  		ContentMD5    string
    52  		SliceMD5      string
    53  		ContentCrc32  string
    54  	}
    55  
    56  	uploadJSON struct {
    57  		*PathJSON
    58  		*pcserror.PCSErrInfo
    59  	}
    60  
    61  	uploadTmpFileJSON struct {
    62  		MD5 string `json:"md5"`
    63  		*pcserror.PCSErrInfo
    64  	}
    65  
    66  	uploadPrecreateJSON struct {
    67  		ReturnType int    `json:"return_type"` // 1上传, 2秒传
    68  		UploadID   string `json:"uploadid"`
    69  		BlockList  []int  `json:"block_list"`
    70  		*pcserror.PanErrorInfo
    71  		fdJSON `json:"info"`
    72  	}
    73  
    74  	uploadCreateJSON struct {
    75  		ErrNo int    `json:"errno"` // 0成功, 2失败
    76  		Path  string `json:"path"`
    77  		*pcserror.PanErrorInfo
    78  	}
    79  
    80  	// UploadSeq 分片上传顺序
    81  	UploadSeq struct {
    82  		Seq   int
    83  		Block string
    84  	}
    85  
    86  	// PrecreateInfo 预提交文件消息返回数据
    87  	PrecreateInfo struct {
    88  		IsRapidUpload bool
    89  		UploadID      string
    90  		UploadSeqList []*UploadSeq
    91  	}
    92  
    93  	uploadSuperfile2JSON struct {
    94  		MD5 string `json:"md5"`
    95  		*pcserror.PCSErrInfo
    96  	}
    97  )
    98  
    99  func randomifyMD5(md5 string) string {
   100  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
   101  	newmd5bytes := []byte(strings.ToLower(md5))
   102  	uppermd5 := []byte(strings.ToUpper(md5))
   103  	for i := range md5 {
   104  		if r.Float32() > 0.6 {
   105  			newmd5bytes[i] = uppermd5[i]
   106  		}
   107  	}
   108  	return string(newmd5bytes)
   109  }
   110  
   111  // RapidUpload 秒传文件
   112  func (pcs *BaiduPCS) RapidUpload(targetPath, contentMD5, sliceMD5, dataContent, crc32 string, offset, length, totalSize, dataTime int64) (pcsError pcserror.Error) {
   113  	defer func() {
   114  		if pcsError == nil {
   115  			// 更新缓存
   116  			pcs.deleteCache([]string{path.Dir(targetPath)})
   117  		}
   118  	}()
   119  	pcsError = pcs.rapidUploadV2(targetPath, strings.ToLower(contentMD5), strings.ToLower(sliceMD5), dataContent, crc32, offset, length, totalSize, dataTime)
   120  	return
   121  }
   122  
   123  // APIRapidUpload openapi秒传文件
   124  func (pcs *BaiduPCS) APIRapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (pcsError pcserror.Error) {
   125  	defer func() {
   126  		if pcsError == nil {
   127  			// 更新缓存
   128  			pcs.deleteCache([]string{path.Dir(targetPath)})
   129  		}
   130  	}()
   131  	pcsError = pcs.rapidUpload(targetPath, strings.ToLower(contentMD5), strings.ToLower(sliceMD5), "", length)
   132  	return
   133  }
   134  
   135  func (pcs *BaiduPCS) rapidUpload(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (pcsError pcserror.Error) {
   136  	dataReadCloser, pcsError := pcs.PrepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32, length)
   137  	if pcsError != nil {
   138  		return
   139  	}
   140  	defer dataReadCloser.Close()
   141  	return pcserror.DecodePanJSONError(OperationRapidUpload, dataReadCloser)
   142  }
   143  
   144  func (pcs *BaiduPCS) rapidUploadV2(targetPath, contentMD5, sliceMD5, dataContent, crc32 string, offset, length, totalSize, dataTime int64) (pcsError pcserror.Error) {
   145  	dataReadCloser, pcsError := pcs.PrepareRapidUploadV2(targetPath, contentMD5, sliceMD5, dataContent, crc32, offset, length, totalSize, dataTime)
   146  	if pcsError != nil {
   147  		return
   148  	}
   149  	defer dataReadCloser.Close()
   150  	return pcserror.DecodeXPanJSONError(OperationRapidUpload, dataReadCloser)
   151  }
   152  
   153  // RapidUploadNoCheckDir 秒传文件, 不进行目录检查, 会覆盖掉同名的目录!
   154  func (pcs *BaiduPCS) RapidUploadNoCheckDir(targetPath, contentMD5, sliceMD5, crc32 string, length int64) (pcsError pcserror.Error) {
   155  	dataReadCloser, pcsError := pcs.prepareRapidUpload(targetPath, contentMD5, sliceMD5, crc32, length)
   156  	if pcsError != nil {
   157  		return
   158  	}
   159  
   160  	defer dataReadCloser.Close()
   161  
   162  	pcsError = pcserror.DecodePCSJSONError(OperationRapidUpload, dataReadCloser)
   163  	if pcsError != nil {
   164  		return
   165  	}
   166  
   167  	return nil
   168  }
   169  
   170  // Upload 上传单个文件
   171  func (pcs *BaiduPCS) Upload(policy, targetPath string, uploadFunc UploadFunc) (pcsError pcserror.Error, newpath string) {
   172  	dataReadCloser, pcsError := pcs.PrepareUpload(policy, targetPath, uploadFunc)
   173  	if pcsError != nil {
   174  		return pcsError, ""
   175  	}
   176  
   177  	defer dataReadCloser.Close()
   178  
   179  	// 数据处理
   180  	jsonData := uploadJSON{
   181  		PCSErrInfo: pcserror.NewPCSErrorInfo(OperationUpload),
   182  	}
   183  
   184  	pcsError = pcserror.HandleJSONParse(OperationUpload, dataReadCloser, &jsonData)
   185  	if pcsError != nil {
   186  		return
   187  	}
   188  
   189  	if jsonData.Path == "" {
   190  		jsonData.PCSErrInfo.ErrType = pcserror.ErrTypeInternalError
   191  		jsonData.PCSErrInfo.Err = ErrUploadSavePathFound
   192  		return jsonData.PCSErrInfo, ""
   193  	}
   194  
   195  	// 更新缓存
   196  	pcs.deleteCache([]string{path.Dir(targetPath)})
   197  	return nil, jsonData.Path
   198  }
   199  
   200  // UploadTmpFile 分片上传—文件分片及上传
   201  func (pcs *BaiduPCS) UploadTmpFile(uploadFunc UploadFunc) (md5 string, pcsError pcserror.Error) {
   202  	dataReadCloser, pcsError := pcs.PrepareUploadTmpFile(uploadFunc)
   203  	if pcsError != nil {
   204  		return "", pcsError
   205  	}
   206  
   207  	defer dataReadCloser.Close()
   208  
   209  	// 数据处理
   210  	jsonData := uploadTmpFileJSON{
   211  		PCSErrInfo: pcserror.NewPCSErrorInfo(OperationUploadTmpFile),
   212  	}
   213  
   214  	pcsError = pcserror.HandleJSONParse(OperationUploadTmpFile, dataReadCloser, &jsonData)
   215  	if pcsError != nil {
   216  		return
   217  	}
   218  
   219  	// 未找到md5
   220  	if jsonData.MD5 == "" {
   221  		jsonData.PCSErrInfo.ErrType = pcserror.ErrTypeInternalError
   222  		jsonData.PCSErrInfo.Err = ErrUploadMD5NotFound
   223  		return "", jsonData.PCSErrInfo
   224  	}
   225  
   226  	return jsonData.MD5, nil
   227  }
   228  
   229  // UploadCreateSuperFile 分片上传—合并分片文件
   230  func (pcs *BaiduPCS) UploadCreateSuperFile(policy string, checkDir bool, targetPath string, blockList ...string) (pcsError pcserror.Error) {
   231  	dataReadCloser, pcsError := pcs.PrepareUploadCreateSuperFile(policy, checkDir, targetPath, blockList...)
   232  	if pcsError != nil {
   233  		return pcsError
   234  	}
   235  
   236  	defer dataReadCloser.Close()
   237  
   238  	errInfo := pcserror.DecodePCSJSONError(OperationUploadCreateSuperFile, dataReadCloser)
   239  	if errInfo != nil {
   240  		return errInfo
   241  	}
   242  
   243  	// 更新缓存, targetPath取了dir所以不受重命名策略影响
   244  	pcs.deleteCache([]string{path.Dir(targetPath)})
   245  	return nil
   246  }
   247  
   248  // UploadPrecreate 分片上传—Precreate,
   249  // 支持检验秒传
   250  func (pcs *BaiduPCS) UploadPrecreate(targetPath, contentMD5, sliceMD5, crc32 string, size int64, bolckList ...string) (precreateInfo *PrecreateInfo, pcsError pcserror.Error) {
   251  	dataReadCloser, pcsError := pcs.PrepareUploadPrecreate(targetPath, contentMD5, sliceMD5, crc32, size, bolckList...)
   252  	if pcsError != nil {
   253  		return
   254  	}
   255  
   256  	defer dataReadCloser.Close()
   257  
   258  	errInfo := pcserror.NewPanErrorInfo(OperationUploadPrecreate)
   259  	jsonData := uploadPrecreateJSON{
   260  		PanErrorInfo: errInfo,
   261  	}
   262  
   263  	pcsError = pcserror.HandleJSONParse(OperationUploadPrecreate, dataReadCloser, &jsonData)
   264  	if pcsError != nil {
   265  		return
   266  	}
   267  
   268  	switch jsonData.ReturnType {
   269  	case 1: // 上传
   270  		seqLen := len(jsonData.BlockList)
   271  		if seqLen != len(bolckList) {
   272  			errInfo.ErrType = pcserror.ErrTypeRemoteError
   273  			errInfo.Err = ErrUploadSeqNotMatch
   274  			return nil, errInfo
   275  		}
   276  
   277  		seqList := make([]*UploadSeq, 0, seqLen)
   278  		for k, seq := range jsonData.BlockList {
   279  			seqList = append(seqList, &UploadSeq{
   280  				Seq:   seq,
   281  				Block: bolckList[k],
   282  			})
   283  		}
   284  		return &PrecreateInfo{
   285  			UploadID:      jsonData.UploadID,
   286  			UploadSeqList: seqList,
   287  		}, nil
   288  
   289  	case 2: // 秒传
   290  		return &PrecreateInfo{
   291  			IsRapidUpload: true,
   292  		}, nil
   293  
   294  	default:
   295  		panic("unknown returntype")
   296  	}
   297  }
   298  
   299  // UploadSuperfile2 分片上传—Superfile2
   300  func (pcs *BaiduPCS) UploadSuperfile2(uploadid, targetPath string, partseq int, partOffset int64, uploadFunc UploadFunc) (md5sum string, pcsError pcserror.Error) {
   301  	dataReadCloser, pcsError := pcs.PrepareUploadSuperfile2(uploadid, targetPath, partseq, partOffset, uploadFunc)
   302  	if pcsError != nil {
   303  		return
   304  	}
   305  
   306  	defer dataReadCloser.Close()
   307  
   308  	jsonData := uploadSuperfile2JSON{
   309  		PCSErrInfo: pcserror.NewPCSErrorInfo(OperationUploadSuperfile2),
   310  	}
   311  
   312  	pcsError = pcserror.HandleJSONParse(OperationUploadSuperfile2, dataReadCloser, &jsonData)
   313  	if pcsError != nil {
   314  		return
   315  	}
   316  
   317  	return jsonData.MD5, nil
   318  }