个人使用腾讯云Cos上传文件功能优化思路分享

2024-03-07 16:50:07 浏览数 (2)

前言

近期笔者在写一个网盘项目时需要使用到腾讯云的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
}

结尾

感谢你看到这里,如果这篇文章对你有帮助的话不妨点个赞,期待下次再见。

0 人点赞