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

实现文件秒传

文件的校验值计算

校验算法类型 校验码长度

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
}

在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"))
		}

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
}

在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)
}

在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
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值