这是我参与「第五届青训营 」伴学笔记创作活动的第 15 天
在青训营大项目的开发中,会有需要提供一个静态文件地址的需求。例如提供视频、封面的http直链。一般情况下我们虽然可以使用Nginx或者其他类似的http服务器直接提供静态文件服务。但是这样提供的服务,视频、图片等静态资源会受到该服务器上传网速的影响, 造成访问缓慢的问题。
因此,我们可以使用对象存储服务,专门存储此类静态文件。
服务商
对象存储有很多主流云计算服务商都有提供,这里我们使用的是腾讯云的COS服务。
注册好服务商账号之后,开通COS服务,创建存储桶,获取cos地址和appid以及密钥。
SDK
我们可以使用服务商提供的SDK来快速构建工具函数,提供文件的上传,下载服务。
这里我们使用的腾讯云的cos的sdk
go get github.com/tencentyun/cos-go-sdk-v5
代码编写
首先新建一个文件,存储好存储桶地址、appid、secret,并定义*cos.client变量,它是cos上传的客户端类。
var (
client *cos.Client
addr string
secretId string
secretKey string
)
接下来,通过cos.NewClient来创建cos客户端,并将其赋值给client变量
// GetClient 返回cos client
func GetClient() *cos.Client {
u, _ := url.Parse(addr)
b := &cos.BaseURL{BucketURL: u}
client := cos.NewClient(b, &http.Client{
Transport: &cos.AuthorizationTransport{
SecretID: secretId, // 用户的 SecretId
SecretKey: secretKey, // 用户的 secretKey
},
})
return client
}
上传
通过client.Object.Put函数,将io.Reader中的文件内容上传到服务器,其中key是文件上传的目录和文件名。
// UploadFile 上传文件
func UploadFile(key string, file io.Reader) error {
_, err := client.Object.Put(
context.Background(), key, file, nil,
)
return err
}
访问URL
要访问对象存储中的文件,可以使用签名URL直接访问。签名URL都有有效时间,超过有效时间就需要重新签名。
ctx := context.Background()
presignedUrl, err := client.Object.GetPresignedURL(ctx, http.MethodGet, key, secretId, secretKey, time.Hour, nil)
其中上面的time.Hour就代表了签名的有效时间。
但是如果每次需要获取url的时候,都要重新签名,就会很浪费系统资源。因为每一个url都可以使用1小时,这时我们可以配合redis做一个缓存,示例代码如下:
// GetSignUrl 返回预签名Url
func GetSignUrl(key string) string {
ctx := context.Background()
redisConn := models.GetRedis()
defer redisConn.Close()
redisKey := common.RedisPrefixCos + common.RedisKeySplit + key
signedUrl, err := redis.String(redisConn.Do("GET", redisKey))
if err == nil && signedUrl != "" {
return signedUrl
}
presignedUrl, err := client.Object.GetPresignedURL(ctx, http.MethodGet, key, secretId, secretKey, time.Hour, nil)
if err != nil {
log.Println(err)
return ""
}
err = redisConn.Send("SET", redisKey, presignedUrl.String())
if err != nil {
log.Println(err)
return ""
}
err = redisConn.Send("EXPIRE", redisKey, 60*60-1)
if err != nil {
log.Println(err)
return ""
}
_, err = redisConn.Do("")
if err != nil {
log.Println(err)
return ""
}
redisConn.Close()
return presignedUrl.String()
}
我们将这个文件的路径(key),存储到Redis中的某个字段下,这样每次要获取签名的时候,都优先访问Redis查找是否已经有签名的URL,如果有URL就直接返回。
如果没有或者已经过期,就重新签名,并且将新的url存储到redis中,并设置过期时间为3599秒(因为1小时是3600秒,少一秒防止拿到的时候就是过期的)。