⭐️ 我把它分为了两个步骤一个是获取临时密钥,一个是获取预签名的URL
1️⃣ 获取临时密钥
需要通过 sts
包来创建 Client
,注意这里不是 go-sdk
包,而是 sts-sdk
包,填写固定的 SecretID
和 SecretKey
,
c := sts.NewClient(
// 通过环境变量获取密钥, os.Getenv 方法表示获取环境变量
os.Getenv("SECRETID"), //这里是填写固定的
os.Getenv("SECRETKEY"),//这里是填写固定的
nil,
)
填写创建密钥的 Options
,
opt := &sts.CredentialOptions{
DurationSeconds: int64(time.Hour.Seconds()), //持续时间
Region: "ap-hongkong", //区域
Policy: &sts.CredentialPolicy{ //Policy
Statement: []sts.CredentialPolicyStatement{
{
Action: []string{ //权限
"name/cos:PostObject",
"name/cos:PutObject",
"name/cos:GetObject",
},
Effect: "allow", //有 allow (允许)和 deny (显式拒绝)两种情况
Resource: []string{ //授权操作的具体数据,可以是任意资源、指定路径前缀的资源、指定绝对路径的资源或它们的组合
//这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
//存储桶的命名格式为 BucketName-APPID,此处填写的 bucket 必须为此格式
"qcs::cos: ap-hongkong:uid/" + os.Getenv("APPID") + ":" + os.Getenv("BUCKET") + "/upload/video/*",
},
},
},
},
}
随后通过 GetCredential
获取临时密钥,查看`Credentials结构,它里面已经包含了获取的临时信息了,在对接cos时会用到
type Credentials struct {
TmpSecretID string `json:"TmpSecretId,omitempty"`
TmpSecretKey string `json:"TmpSecretKey,omitempty"`
SessionToken string `json:"Token,omitempty"`
}
下面时完整代码
import (
"context"
"github.com/tencentyun/cos-go-sdk-v5"
sts "github.com/tencentyun/qcloud-cos-sts-sdk/go"
"net/http"
"net/url"
"os"
"time"
)
// CosToken 获取腾讯云cos临时密钥
func CosToken() (*sts.Credentials, error) {
c := sts.NewClient(
// 通过环境变量获取密钥, os.Getenv 方法表示获取环境变量
os.Getenv("SECRETID"),
os.Getenv("SECRETKEY"),
nil,
)
// 策略概述 https://cloud.tencent.com/document/product/436/18023
opt := &sts.CredentialOptions{
DurationSeconds: int64(time.Hour.Seconds()),
Region: [这里需要填写区域名]如ap-chengdu,
Policy: &sts.CredentialPolicy{
Statement: []sts.CredentialPolicyStatement{
{
Action: []string{
"name/cos:PostObject",
"name/cos:PutObject",
"name/cos:GetObject",
},
Effect: "allow",
Resource: []string{
//这里改成允许的路径前缀,可以根据自己网站的用户登录态判断允许上传的具体路径,例子: a.jpg 或者 a/* 或者 * (使用通配符*存在重大安全风险, 请谨慎评估使用)
//存储桶的命名格式为 BucketName-APPID,此处填写的 bucket 必须为此格式
"qcs::cos: ap-hongkong:uid/" + os.Getenv("APPID") + ":" + os.Getenv("BUCKET") + "/upload/video/*",
},
},
},
},
}
res, err := c.GetCredential(opt)
if err != nil {
return nil, err
}
return res.Credentials, nil
}
2️⃣ 生成预签名(对接 cos
)
我这里配合了 gin
的自定义验证器,接收一个 FileName
首先调用你获取临时密钥函数,来获取临时密钥
token, err := utils.CosToken()
if err != nil {
logrus.Error("获取临时密钥失败", err.Error())
return gin.H{
"code": -1,
"msg": "获取临时密钥失败",
}
}
创建对接 cos
的 client
,它接收一个 BaseUrl
和一个 http.client
,这里的 url
是存储桶的访问地址
u, _ := url.Parse("https://存储桶名.cos.区域.myqcloud.com")
b := &cos.BaseURL{BucketURL: u}
c := cos.NewClient(b, &http.Client{})
这里获取文件后缀名后,通过 uuid+后缀名
的方式来生成 key
,这个 key
,可以理解为对象上传后存储的位置
//获取文件后缀
ext := filepath.Ext(service.FileName)
// 对象键(Key)是对象在存储桶中的唯一标识。
// 例如,在对象的访问域名 `examplebucket-1250000000.cos.COS_REGION.myqcloud.com/test/objectPut.go` 中,对象键为 test/objectPut.go
//key := "upload/video/" + service.FileName
key := "upload/video/" + utils.GenerateUUID() + ext
注意预签名
url
的method
,需要在临时token
中的Action
中允许
生成上传(put
)预签名 url
,通过,Option
设置接收的 vaule
和头文件,随后通过获取的后缀来判断文件传输的类型,在通过 Query.Add
来添加 token
第一个参数指定上下文的 Background
,第二个参数指定 url
的 method
这里的 SeesionToken
、SecretID
、SecretKey
是需要获取临时 token
内的,然后就是超时时间,最后时 options
opt := &cos.PresignedURLOptions{
Query: &url.Values{},
Header: &http.Header{
//设置上传文件类型
"Content-Type": []string{mime.TypeByExtension(ext)},
},
}
//添加session
opt.Query.Add("x-cos-security-token", token.SessionToken)
//获取上传文件预签名Url
putURL, err := c.Object.GetPresignedURL(context.Background(), http.MethodPut, key, token.TmpSecretID, token.TmpSecretKey, time.Hour, opt)
if err != nil {
logrus.Error("上传失败", err.Error())
return gin.H{
"code": -1,
"msg": "获取上传预签名失败",
}
}
Get
预签名也是同理,只不过不需要添加文件类型
opt2 := &cos.PresignedURLOptions{
Query: &url.Values{},
Header: &http.Header{},
}
//添加session
opt2.Query.Add("x-cos-security-token", token.SessionToken)
//获取查看文件预签名Url
getURL, errGet := c.Object.GetPresignedURL(context.Background(), http.MethodGet, key, token.TmpSecretID, token.TmpSecretKey, time.Hour, opt2)
if errGet != nil {
logrus.Error("获取失败", errGet.Error())
return gin.H{
"code": -1,
"msg": "获取下载预签名失败",
}
}
下面时完整代码
import (
"GliGliVideo/utils"
"context"
"github.com/gin-gonic/gin"
"github.com/sirupsen/logrus"
"github.com/tencentyun/cos-go-sdk-v5"
"mime"
"net/http"
"net/url"
"path/filepath"
"time"
)
type VideoUploadSvc struct {
FileName string `json:"file_name" form:"file_name"`
}
func (service *VideoUploadSvc) Post() gin.H {
token, err := utils.CosToken()
if err != nil {
logrus.Error("获取临时密钥失败", err.Error())
return gin.H{
"code": -1,
"msg": "获取临时密钥失败",
}
}
// 将 examplebucket-1250000000 和 COS_REGION 修改为真实的信息
// 存储桶名称,由 bucketname-appid 组成,appid 必须填入,可以在 COS 控制台查看存储桶名称。https://console.cloud.tencent.com/cos5/bucket
// COS_REGION 可以在控制台查看,https://console.cloud.tencent.com/cos5/bucket, 关于地域的详情见 https://cloud.tencent.com/document/product/436/6224
u, _ := url.Parse("https://存储桶名.cos.区域.myqcloud.com")
b := &cos.BaseURL{BucketURL: u}
c := cos.NewClient(b, &http.Client{})
//获取文件后缀
ext := filepath.Ext(service.FileName)
// 对象键(Key)是对象在存储桶中的唯一标识。
// 例如,在对象的访问域名 `examplebucket-1250000000.cos.COS_REGION.myqcloud.com/test/objectPut.go` 中,对象键为 test/objectPut.go
//key := "upload/video/" + service.FileName
key := "upload/video/" + utils.GenerateUUID() + ext
opt := &cos.PresignedURLOptions{
Query: &url.Values{},
Header: &http.Header{
//设置上传文件类型
"Content-Type": []string{mime.TypeByExtension(ext)},
},
}
//添加session
opt.Query.Add("x-cos-security-token", token.SessionToken)
//获取上传文件预签名Url
putURL, err := c.Object.GetPresignedURL(context.Background(), http.MethodPut, key, token.TmpSecretID, token.TmpSecretKey, time.Hour, opt)
if err != nil {
logrus.Error("上传失败", err.Error())
return gin.H{
"code": -1,
"msg": "获取上传预签名失败",
}
}
opt2 := &cos.PresignedURLOptions{
Query: &url.Values{},
Header: &http.Header{},
}
//添加session
opt2.Query.Add("x-cos-security-token", token.SessionToken)
//获取查看文件预签名Url
getURL, errGet := c.Object.GetPresignedURL(context.Background(), http.MethodGet, key, token.TmpSecretID, token.TmpSecretKey, time.Hour, opt2)
if errGet != nil {
logrus.Error("获取失败", errGet.Error())
return gin.H{
"code": -1,
"msg": "获取下载预签名失败",
}
}
return gin.H{
"code": 200,
"msg": "success",
"key": key,
"get": getURL.String(),
"put": putURL.String(),
}
}