前言
近期笔者在写一个网盘项目时需要使用到腾讯云的cos功能,由于财力不足,不得不对用户的上传功能进行优化,以免一觉睡醒,cos欠费。
以下是笔者优化的思路主要为md5码优化和redis优化,以消耗请求次数的方法优化cos存储容量。
MD5码优化上传
将用户文件名以md5码的格式上传至腾讯云进行存储。在用户上传文件时,计算文件内容的md5码,将其与文件名后缀结合上传至腾讯云cos。
MD5码计算方式参考
代码语言:go复制// GetFileMD5 使用本地文件路径计算本地文件md5码
func GetFileMD5(filePath string) (string, error) {
file, err := os.Open(filePath)
defer func() {
_ = file.Close()
}()
if err != nil {
return "", err
}
hash := md5.New()
// 如果一个文件有相同的内容但是后缀不同时,仅计算文件内容得到的md5码是相同的
// 用户在下载时可能下载到相同内容但是不同后缀的文件,需补上文件后缀计算md5码
ext := path.Ext(file.Name())
hash.Write([]byte(ext))
_, _ = io.Copy(hash, file)
return hex.EncodeToString(hash.Sum(nil)), nil
}
在随后用户再次上传相同类型文件时,先使用腾讯云的接口判断文件是否上传过用户文件夹,如上传过则放弃上传。
上传接口参考
代码语言:go复制// 此处使用简单上传对象接口进行上传
func (cloud *TencentCloudDisk) UploadSimpleFile(localFilePath string, userId string, md5 string, fileSize int64) error {
// 判断文件大小,简单接口最大仅支持5GB数据
if fileSize/1024/1024/1024 > 1 {
return fmt.Errorf("file to big, please use uploadfile")
}
// 调用腾讯云提供的接口检查是否上传过该文件
extend := path.Ext(localFilePath)
ok, err := cloud.IsObjectExist(userId, "", md5 extend)
if err != nil {
return err
}
// 如果不存在,则直接调用接口上传
if !ok {
key := fastBuildKey(userId, "", md5 extend)
if err = cloud.uploadSimpleFile(localFilePath, key); err != nil {
return err
}
}
return nil
}
检查文件是否在云端接口参考
代码语言:go复制func (cloud *TencentCloudDisk) checkObjectIsExist(key string) (bool, error) {
// 使用 bucketname,secretId,secretKey生成默认client
client := cloud.getDefaultClient()
// 调用腾讯云接口
ok, err := client.Object.IsExist(context.Background(), key)
if err != nil {
return false, err
}
return ok, nil
}
// IsObjectExist 快速构建key调用接口查询
func (cloud *TencentCloudDisk) IsObjectExist(userId string, filePath string, fileName string) (bool, error) {
key := fastBuildKey(userId, filePath, fileName)
ok, err := cloud.checkObjectIsExist(key)
return ok, err
}
简单上传接口参考
代码语言:go复制func (cloud *TencentCloudDisk) uploadSimpleFile(localFilePath string, key string) error {
client := cloud.getDefaultClient()
opt := &cos.ObjectPutOptions{
ObjectPutHeaderOptions: &cos.ObjectPutHeaderOptions{
ContentDisposition: "attachment", // 添加上这段时调用getobject 默认下载
},
}
// 使用官方api接口
_, err := client.Object.PutFromFile(context.Background(), key, localFilePath, opt)
if err != nil {
return err
}
return nil
}
构建结构体参考
代码语言:go复制type TencentCloudDisk struct {
bucketname string
secretId string
secretKey string
}
func (cloud *TencentCloudDisk) getDefaultClient() *cos.Client {
u, _ := url.Parse(cloud.bucketname)
b := &cos.BaseURL{BucketURL: u}
c := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
SecretID: cloud.secretId,
SecretKey: cloud.secretKey,
},
})
return c
}
Redis优化文件上传
在上传文件时遇到重复文件时仍需调用腾讯云接口,如果想要少调用远程接口可以使用redis。
使用redis记录上传文件的md5码,将多个用户上传的相同文件索引至相同的云文件,避免重复上传。同时方便后续管理员进行文件封禁。
使用redis优化参考
代码语言:go复制//计算文件md5码
md5String, err := GetFileMD5(dst)
if err != nil {
return serializer.ParamsErr("file err", err)
}
// 使用md5码查询redis近期是否上传文件,如果上传过对应文件则直接跳过腾讯云查询
filePath := GetFileInfoFromRedis(md5String)
if filePath == "" {
// 近期没有人调用过该文件,上传至云服务器
err = disk.BaseCloudDisk.UploadSimpleFile(dst, userId, md5String, file.Size)
if err != nil {
return serializer.DBErr("can't upload to cloud", err)
}
filePath = userId
}
....
// 保存文件信息到redis
SaveFileUploadInfoToRedis()
Redis保存文件信息参考,以go-redis为例
代码语言:go复制// 生成对应key
func FileInfoStoreKey(id string) string {
return fmt.Sprintf("file:cloud:%s", id)
}
// 保存文件
func SaveFileUploadInfoToRedis() {
// 随机过期时间,减少缓存雪崩几率
randTime := time.Hour*12 time.Minute*time.Duration(rand.Intn(60))
cache.RedisClient.Set(context.Background(), cache.FileInfoStoreKey(file.FileUuid), file.FilePath, randTime)
}
获取redis文件
代码语言:go复制func GetFileInfoFromRedis(md5 string) string {
filePath := cache.RedisClient.Get(context.Background(), cache.FileInfoStoreKey(md5)).Val()
return filePath
}
结尾
感谢你看到这里,如果这篇文章对你有帮助的话不妨点个赞,期待下次再见。