分块上传和断点续传
两个概念
分块上传:文件切成多块,独立传输,上传完成后合并
断点续传:传输暂停或异常中断后,可基于原来进度重传
几点说明:
1、小文件不建议分块上传
2、可以并行上传,并且可以无序传输
3、分块上传可以极大提高传输效率,不过要注意分块上传文件的数量
4、减少传输失败后重试的流量及时间
流程:
1、云端初始化上传文件的信息
2、客户端执行上传分块—>上传取消,查询上传信息
3、客户端通知云端上传完成
服务架构:
redis缓存用于云端与客户端文件信息交互
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5PJcYLvX-1641174946216)(E:学习笔记Go笔记截图屏幕截图 2022-01-02 095031.png)]
分块上传通用接口
1、初始化分块信息
2、上传分块
3、通知上传完成
4、取消上传分块
5、查看分块上传的整体状态
前期准备
在src下创建cache,在cache目录下再创建Redis,在Redis目录下创建conn.go
import( "fmt" "github.com/garyburd/redigo/redis" "time" ) var( pool*redis.Pool//redis连接池用于客户端与redis交互数据 redisHost="127.0.0.1:6379"//redis的IP redisPass="testupload"//redis登录密码 ) func newRedisPool()*redis.Pool{//初始化redis return&redis.Pool{ MaxIdle:50,//最大存储文件信息数 MaxActive:30,//最多实际存储文件信息数 IdleTimeout:300*time.Second,//超过该时间断开与redis的连接 Dial:func()(redis.Conn,error){ //1.打开连接 c,err:=redis.Dial("tcp",redisHost) if err!=nil{ fmt.Println(err) return nil,err } //2.访问认证 if _,err=c.Do("AUTH",redisPass);err!=nil{ c.Close() return nil,err } return c,nil }, //定时检查redis的健康状况,若出问题则在客户端关闭redis的连接 TestOnBorrow:func(conn redis.Conn,t time.Time)error{ if time.Since(t)1、初始化分块信息
import ( "fmt" "math" "net/http" "rgo/src/util" "strconv" rPool"rgo/cache/redis" "time" ) //初始化信息 type MultipartUploadInfo struct{ FileHash string FileSize int UploadID string //唯一标识 ChunkSize int //分块大小 ChunkCount int //分块个数 } func InitialMultipartUpload(w http.ResponseWriter,r*http.Request){ r.ParseForm() username:=r.Form.Get("username") filehash:=r.Form.Get("filehash") filesize,err:=strconv.Atoi(r.form.Get("filesize")) if err!=nil{ w.Write(util.NewRespMsg(-1,"params invalid",nil).JSONBytes()) return } //2。获得redis的一个连接 rConn:=rPool.RedisPool().Get() defer rConn.Close() //3.生成分块上传的初始化信息 upInfo:=MultipartUploadInfo{ FileHash:filehash, FileSize:filesize, UploadID:username+fmt.Sprintf("%x",time.Now().UnixNano()), ChunkSize:5*1024*1024,//5MB ChunkCount:int(math.Ceil(float64(filesize)/(5*1024*1024))), } //4.将初始化信息写入到redis缓存 rConn.Do("HEST","MP_"+upInfo.UploadID,"chunkcount",upInfo.ChunkCount) rConn.Do("HEST","MP_"+upInfo.UploadID,"filehash",upInfo.FileHash) rConn.Do("HEST","MP_"+upInfo.UploadID,"filesize",upInfo.FileSize) //5.将响应初始化数据返回到客户端 w.Write(util.NewRespMsg(0,"OK",upInfo).JSONBytes()) }2.上传文件分块
//上传文件分块 func UploadPartHandler(w http.ResponseWriter,r*http.Request){ //1.解析用户请求参数 r.ParseForm() username:=r.Form.Get("username") uploadID:=r.Form.Get("uploadid") chunkIndex:=r.Form.Get("index") //2.获得redis连接池中的一个连接 rConn:=rPool.RedisPool().Get() defer rConn.Close() //3.获得文件句柄,用于存储分块内容 fpath:="/data/"+uploadID+"/"+chunkIndex os.MkdirAll(path.Dir(fpath),0744) if err!=nil{ w.Write(util.NewrESPmSG(-1,"Upload part failed",nil).JSONBytes()) return } defer fd.Close() buf:=make([]byte,1024*1024) for{ n,err:=r.Body.Read(buf) fd.Write(buf[:n]) if err!=nil{ break } } //4.更新redis缓存状态 rConn.Do("HEST","MP_"+uploadID,"chkid_"+chunkIndex,1) //5.返回处理结果到客户端 w.Write(util.NewrESPmSG(0,"OK",nil).JSONBytes()) }3.合并
//通知上传合并接口 func CompleteUploadHandler(w http.ResponseWriter,r*http.Request){ //1.解析请求参数 r.ParseForm() upid:=r.Form.Get("uploadid") username:=r.Form.Get("username") filehash:=r.Form.Get("filehash") filesize:=r.Form.Get("filesize") filename:=r.Form.Get("filename") //2.获得redis连接池中的一个连接 rConn:=rPool.RedisPool().Get() defer rConn.Close() //3.通过uploadid查询redis并判断是否所有分块上传完成 data,err:=redis.Values(rConn.Do("HGETALL","MP_"+upid)) if err!=nil{ w.Write(util.NewRespMsg(-1,"complete upload failed",nil).JSONBytes()) return } totalCount:=0 chunkCount:=0 for i:=0;ii(filesize)
dblayer.onFileUploadFinished(filehash,filename,int(fsize),"")
dblayer.onUserFileUploadFinished(username,filehash,filename,int64(fsize))
//6.响应处理结果
w.Write(util.NewRespMsg(0,“OK”,nil).JSonBytes())
}欢迎分享,转载请注明来源:内存溢出
评论列表(0条)