• 分布式文件存储——文件秒传


    实现文件秒传

    文件的校验值计算

    校验算法类型 校验码长度

    1.CRC 4/8字节 计算效率高但安全性较低 传输数据的校验

    2.MD5 16个字节 中等 文件校验和数据签名

    3.SHA1 20个字节 安全性高 文件校验和数据签名

    场景

    1.用户上传

    2.离线下载

    3.好友分享

    关键点

    1.文件的Hash值(MD5,Sha1)

    每次文件上传到云存储服务,会自动计算文件的HASH值,下一次用户上传,只要Hash值相同就可以省区重复上传,客户端计算文件Hash值传到云端进行对比才可以实现秒传

    2.用户文件的关联

    建立唯一文件表用于存储唯一的文件的信息,而用户文件表存储每个用户的文件的信息,包括重复文件

    文件秒传服务架构

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2npiPqgx-1659837717597)(C:/Users/86158/AppData/Roaming/Typora/typora-user-images/image-20220807095740398.png)]

    如图

    1. 用户上传文件到上传server
    2. 上传server将文件存储到本地
    3. 调用Hash计算文件Hash值
    4. 将文件信息存储到唯一文件表
    5. 将文件信息关联到用户文件表

    如果计算出文件Hash值比对成功则减少上传实现秒传

    在db/下创建db/userFile,并创建用户文件结构体和用户文件表的更新

    import (
    	mydb "rgo/src/db/mysql"
    	"time"
    )
    
    //用户文件表的结构体
    type UserFile struct{
    	UserName string
    	FileHash string
    	FileName string
    	FileSize int64
    	UploadAt string
    	LastUpdated string
    }
    //更新用户文件表
    func OnUserFileUploadFinished(username,filehash,filename string,filesize int64)bool{
    	stmt,err:=mydb.DBConn().Prepare("" +
    		"insert ignore into tbl_user_file(`user_name`,`file_sha1`,`file_name`,"+
    		"`file_size`,`upload_at`)values(?,?,?,?,?)")
    	if err!=nil{
    		return false
    	}
    	defer stmt.Close()
    	_,err=stmt.Exec(username,filehash,filename,filesize,time.Now())
    	if err!=nil{
    		return false
    	}
    	return true
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30

    在handler/handler.go的UploadHandler中修改

    //更新用户文件表记录
    		r.ParseForm()
    		username := r.Form.Get("username")
    		suc := dblayer.OnUserFileUploadFinished(username, fileMeta.FileShal,
    			fileMeta.FileName, fileMeta.FileSize)
    		if suc {
    			http.Redirect(w, r, "/file/upload/suc", http.StatusFound)
    		} else {
    			w.Write([]byte("Upload Failed"))
    		}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    db/userFile下创建函数

    //批量获取用户文件信息
    func QueryUserFileMetas(username string,limit int)([]UserFile,error){
    	stmt,err:=mydb.DBConn().Prepare(
    		"select file_sha1,file_name,file_size,upload_at,last_update from"+
    			"tbl_user_file where user_name=? limit ?")
    	if err!=nil{
    		return nil,err
    	}
    	defer stmt.Close()
    	rows,err:=stmt.Query(username,limit)
    	if err!=nil{
    		return nil,err
    	}
    	var userFiles []UserFile
    	for rows.Next(){//从数据库查找的表中将文件信息复制到[]UserFile作为返回
    		ufile:=UserFile{}
    		err=rows.Scan(&ufile.FileHash,&ufile.FileName,&ufile.FileSize,
    			&ufile.UploadAt,&ufile.LastUpdated)
    		if err!=nil{
    			fmt.Println(err.Error())
    			break
    		}
    		userFiles=append(userFiles,ufile)
    	}
    	return userFiles,nil
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    在handler/handler.go修改接口

    //查询批量的文件元信息
    func FileQueryHandler(w http.ResponseWriter,r*http.Request){
    	r.ParseForm()
    	limitCnt,_:=strconv.Atoi(r.Form.Get("limit"))
    	username:=r.Form.Get("username")
    	userFiles,err:=dblayer.QueryUserFileMetas(username,limitCnt)
    	if err!=nil{
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    	data,err:=json.Marshal(userFiles)
    	if err!=nil{
    		w.WriteHeader(http.StatusInternalServerError)
    		return
    	}
    	w.Write(data)
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    在handler/handler.go中编写

    //尝试秒传接口
    func TryFastUploadHandler(w http.ResponseWriter,r*http.Request){
    	r.ParseForm()
    	//1.解析请求参数
    	username:=r.Form.Get("username")
    	filehash:=r.Form.Get("filehash")
    	filename:=r.Form.Get("filename")
    	filesize,_:=strconv.Atoi(r.Form.Get("filesize"))
    	//从文件表中查询相同hash文件记录
        fileMeta,err:=meta.GetFileMetaDB(filehash)
        if err!=nil{
        	fmt.Println(err.Error())
        	w.WriteHeader(http.StatusInternalServerError)
        	return
    	}
    	//查找不到记录则返回秒传失败
         if fileMeta==nil{
         	resp:=util.RespMsg{
         		Code:-1,
         		Msg:"秒传失败,请访问普通上传接口",
    		}
    		w.Write(resp.JSONBytes())
         	return
    	 }
    	//上传文过件则将文件信息写入用户文件表,返回成功
    	suc:=dblayer.OnUserFileUploadFinished(username,filehash,filename,int64(filesize))
    	if suc{
    		resp:=util.RespMsg{
    			Code:0,
    			Msg:"秒传成功",
    		}
    		w.Write(resp.JSONBytes())
    		return
    	}else{
    		resp:=util.RespMsg{
    			Code:-2,
    			Msg:"秒传失败,请稍后重试",
    		}
    		w.Write(resp.JSONBytes())
    		return
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
  • 相关阅读:
    YTM32的电源管理与低功耗系统详解
    《统计学习方法》第三章习题
    Keithley2420吉时利2420数字源表
    动态规划算法:背包问题
    # Toyota Programming Contest 2024#7(AtCoder Beginner Contest 362)
    ctfshow菜狗杯 web 无算力以及easyPytHon_P
    选择边缘计算网关的五大优势
    C# —— 逻辑运算符
    MyBatisPlus的学习项目页面
    C++之策略(Strategy)模式
  • 原文地址:https://blog.csdn.net/qq_53267860/article/details/126206887