github.com/keysonzzz/kmg@v0.0.0-20151121023212-05317bfd7d39/third/kmgQiniu/upload.go (about)

     1  package kmgQiniu
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"path/filepath"
     7  
     8  	"github.com/bronze1man/kmg/kmgTask"
     9  	qiniuIo "github.com/qiniu/api/io"
    10  	"github.com/qiniu/api/rs"
    11  	"github.com/qiniu/rpc"
    12  )
    13  
    14  //上传文件或目录
    15  var Upload = UploadDirMulitThread
    16  
    17  //单线程上传目录
    18  //检查同文件名和同内容的文件是否存在,如果内容相同便不上传
    19  func UploadDir(ctx *Context, localRoot string, remoteRoot string) (err error) {
    20  	return filepath.Walk(localRoot, func(path string, info os.FileInfo, inErr error) (err error) {
    21  		if inErr != nil {
    22  			return inErr
    23  		}
    24  		if info.IsDir() {
    25  			return
    26  		}
    27  		relPath, err := filepath.Rel(localRoot, path)
    28  		if err != nil {
    29  			return fmt.Errorf("[qiniuUploadDir] filepath.Rel err:%s", err.Error())
    30  		}
    31  		remotePath := filepath.Join(remoteRoot, relPath)
    32  		err = UploadFileCheckExist(ctx, path, remotePath)
    33  		if err != nil {
    34  			return fmt.Errorf("[qiniuUploadDir] qiniuUploadFile err:%s", err.Error())
    35  		}
    36  		return
    37  	})
    38  }
    39  
    40  type uploadFileRequest struct {
    41  	localPath  string
    42  	remotePath string
    43  	expectHash string
    44  }
    45  
    46  //多线程上传目录
    47  //1.某个文件仅在一个线程中上传,
    48  //2.检查同名和同内容的文件是否已经存在了,如果存在,且hash相同便不上传(断点续传)
    49  // TODO 解决一边上传,一边修改的bug.
    50  func UploadDirMulitThread(ctx *Context, localRoot string, remoteRoot string) (err error) {
    51  	tm := kmgTask.NewLimitThreadErrorHandleTaskManager(ThreadNum, 3)
    52  	defer tm.Close()
    53  	requestList := []uploadFileRequest{}
    54  	remotePathList := []string{}
    55  	//dispatch task 分配任务
    56  	err = filepath.Walk(localRoot, func(path string, info os.FileInfo, inErr error) (err error) {
    57  		if inErr != nil {
    58  			return inErr
    59  		}
    60  		if info.IsDir() {
    61  			return
    62  		}
    63  		relPath, err := filepath.Rel(localRoot, path)
    64  		if err != nil {
    65  			return fmt.Errorf("[qiniuUploadDir] filepath.Rel err:%s", err.Error())
    66  		}
    67  		remotePath := NormalizeRemotePath(filepath.Join(remoteRoot, relPath))
    68  		expectHash, err := ComputeHashFromFile(path)
    69  		if err != nil {
    70  			return
    71  		}
    72  		requestList = append(requestList, uploadFileRequest{
    73  			localPath:  path,
    74  			remotePath: remotePath,
    75  			expectHash: expectHash,
    76  		})
    77  		remotePathList = append(remotePathList, remotePath)
    78  		return
    79  	})
    80  	if len(requestList) == 0 {
    81  		return ErrNoFile
    82  	}
    83  	batchRet, err := ctx.BatchStat(remotePathList)
    84  	if err != nil {
    85  		return
    86  	}
    87  	for i, ret := range batchRet {
    88  		i := i
    89  		if ret.IsExist() && ret.Hash == requestList[i].expectHash {
    90  			continue
    91  		}
    92  		tm.AddTask(func() (err error) {
    93  			return UploadFileWithHash(ctx, requestList[i].localPath, requestList[i].remotePath, requestList[i].expectHash)
    94  		})
    95  	}
    96  	////群发状态询问消息减少网络连接数量,加快速度
    97  	//entryPathList := make([]rs.EntryPath, len(requestList))
    98  	//for i, req := range requestList {
    99  	//	entryPathList[i].Bucket = ctx.bucket
   100  	//	entryPathList[i].Key = req.remotePath
   101  	//}
   102  	//batchRet, err := ctx.client.BatchStat(nil, entryPathList)
   103  	///*
   104  	//   //此处返回的错误很奇怪,有大量文件不存在信息,应该是正常情况,此处最简单的解决方案就是假设没有错误
   105  	//   if err != nil{
   106  	//       fmt.Printf("%T %#v\n",err,err)
   107  	//       err1,ok:=err.(*rpc.ErrorInfo)
   108  	//       if !ok{
   109  	//           return err
   110  	//       }
   111  	//       if err1.Code!=298{
   112  	//           return err
   113  	//       }
   114  	//   }
   115  	//*/
   116  	//if len(batchRet) != len(entryPathList) {
   117  	//	return fmt.Errorf("[UploadDirMulitThread] len(batchRet)[%d]!=len(entryPathList)[%d] err[%s]",
   118  	//		len(batchRet), len(entryPathList),err)
   119  	//}
   120  	//for i, ret := range batchRet {
   121  	//	i := i
   122  	//	//验证hash,当文件不存在时,err是空
   123  	//	if ret.Error != "" && ret.Error != "no such file or directory" {
   124  	//		return fmt.Errorf("[UploadDirMulitThread] [remotePath:%s]ctx.client.BatchStat err[%s]",
   125  	//			requestList[i].remotePath, ret.Error)
   126  	//	}
   127  	//	if ret.Data.Hash == requestList[i].expectHash {
   128  	//		continue
   129  	//	}
   130  	//	tm.AddTask(func() (err error) {
   131  	//		return UploadFileWithHash(ctx, requestList[i].localPath, requestList[i].remotePath, requestList[i].expectHash)
   132  	//	})
   133  	//}
   134  	tm.Wait()
   135  	if err != nil {
   136  		return err
   137  	}
   138  	err = tm.GetError()
   139  	return
   140  }
   141  
   142  //上传文件,检查同名和同内容文件
   143  //先找cdn上是不是已经有一样的文件了,以便分文件断点续传,再上传
   144  func UploadFileCheckExist(ctx *Context, localPath string, remotePath string) (err error) {
   145  	remotePath = NormalizeRemotePath(remotePath)
   146  	entry, err := ctx.client.Stat(nil, ctx.bucket, remotePath)
   147  	if err != nil {
   148  		if !(err.(*rpc.ErrorInfo) != nil && err.(*rpc.ErrorInfo).Err == "no such file or directory") {
   149  			return err
   150  		}
   151  	}
   152  	expectHash, err := ComputeHashFromFile(localPath)
   153  	if err != nil {
   154  		return
   155  	}
   156  	//already have a file with same context and same key,do nothing
   157  	if entry.Hash == expectHash {
   158  		return
   159  	}
   160  	return UploadFileWithHash(ctx, localPath, remotePath, expectHash)
   161  }
   162  
   163  //上传文件,检查返回的hash和需要的hash是否一致
   164  func UploadFileWithHash(ctx *Context, localPath string, remotePath string, expectHash string) (err error) {
   165  	var ret qiniuIo.PutRet
   166  	var extra = &qiniuIo.PutExtra{
   167  		CheckCrc: 1,
   168  	}
   169  	putPolicy := rs.PutPolicy{
   170  		Scope: ctx.bucket + ":" + remotePath,
   171  	}
   172  	uptoken := putPolicy.Token(nil)
   173  	err = qiniuIo.PutFile(nil, &ret, uptoken, remotePath, localPath, extra)
   174  	//fmt.Println(localPath,remotePath,err)
   175  	if err != nil {
   176  		return
   177  	}
   178  	if ret.Hash != expectHash {
   179  		return fmt.Errorf("[UploadFileWithHash][remotePath:%s] ret.Hash:[%s]!=expectHash[%s] ", remotePath, ret.Hash, expectHash)
   180  	}
   181  
   182  	return
   183  }