主要参考以下文档与代码
后端 STS 服务
接口提供临时密钥和 token,服务端代码如下:
package main
import (
"encoding/json"
"log"
"net/http"
"os"
"strconv"
"time"
"github.com/joho/godotenv"
sts "github.com/tencentyun/qcloud-cos-sts-sdk/go"
)
const DefaultCredExpireTime = 600 // seconds
var (
appId string
bucket string
region string
stsClient *sts.Client
credOpts *sts.CredentialOptions
stsExpireTime int
)
type Credentials struct {
TmpSecretId string `json:"tmpSecretId"`
TmpSecretKey string `json:"tmpSecretKey"`
SessionToken string `json:"sessionToken"`
}
type CredentialsResp struct {
Credentials Credentials `json:"credentials"`
StartTime int64 `json:"startTime"`
ExpiredTime int64 `json:"expiredTime"`
}
func init() {
err := godotenv.Load()
if err != nil {
log.Fatal(err)
}
appId = os.Getenv("COS_APPID")
bucket = os.Getenv("COS_BUCKET")
region = os.Getenv("COS_REGION")
secretId := os.Getenv("COS_SECRET_ID")
secretKey := os.Getenv("COS_SECRET_KEY")
stsExpireTime, err = strconv.Atoi(os.Getenv("STS_EXPIRE_TIME"))
if err != nil {
log.Printf("read enviroment `STS_EXPIRE_TIME` error: %v\n", err)
log.Printf("set `STS_EXPIRE_TIME` to default %v", DefaultCredExpireTime)
stsExpireTime = DefaultCredExpireTime
}
stsClient = sts.NewClient(
secretId,
secretKey,
nil,
)
credOpts = &sts.CredentialOptions{
DurationSeconds: int64(stsExpireTime),
Region: region,
Policy: &sts.CredentialPolicy{
Statement: []sts.CredentialPolicyStatement{
{
Action: []string{
"name/cos:PostObject",
"name/cos:PutObject",
"name/cos:InitiateMultipartUpload",
"name/cos:ListMultipartUploads",
"name/cos:ListParts",
"name/cos:UploadPart",
"name/cos:CompleteMultipartUpload",
},
Effect: "allow",
Resource: []string{
"qcs::cos:ap-guangzhou:uid/" + appId + ":" + bucket + "/*",
},
Condition: map[string]map[string]interface{}{},
},
},
},
}
}
func getCredentialsHandler(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "*")
res, err := stsClient.GetCredential(credOpts)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
w.Header().Set("Content-Type", "application/json")
resp := CredentialsResp{
Credentials: Credentials{
TmpSecretId: res.Credentials.TmpSecretID,
TmpSecretKey: res.Credentials.TmpSecretKey,
SessionToken: res.Credentials.SessionToken,
},
StartTime: time.Now().Unix(),
ExpiredTime: time.Now().Add(time.Duration(stsExpireTime) * time.Second).Unix(),
}
err = json.NewEncoder(w).Encode(resp)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
func main() {
log.Println("RunStsServer starts...")
http.HandleFunc("/credentials", getCredentialsHandler)
http.ListenAndServe(":8080", nil)
}
通过调用接口就可以获得临时密钥
curl "http://127.0.0.1:8080/credentials"
因为要在 Web 调用,因此需要设置允许跨域请求
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, OPTIONS")
w.Header().Set("Access-Control-Allow-Headers", "*")
Web 直传 demo
<template>
<div>
<input type="file" @change="uploadImage" ref="imageInput" />
<button @click="submitImage">Upload Image</button>
</div>
</template>
<script>
import COS from 'cos-js-sdk-v5';
export default {
data() {
return {
imageFile: null,
imageUrl: null,
cos: null,
};
},
methods: {
uploadImage(event) {
this.imageFile = event.target.files && event.target.files[0];
},
submitImage() {
if (!this.imageFile) {
alert("Please choose an image to upload.");
return;
}
this.initCOS();
this.uploadToCOS();
},
initCOS() {
this.cos = new COS({
getAuthorization: function (options, callback) {
const url = 'http://${host}:8080/credentials'
const xhr = new XMLHttpRequest()
let data = null
let credentials = null
xhr.open('GET', url, true)
xhr.onload = function (e) {
try {
data = JSON.parse(e.target.responseText);
credentials = data.credentials;
} catch (e) {
console.log(e)
}
if (!data || !credentials) {
return console.error('credentials invalid:\n' + JSON.stringify(data, null, 2))
}
callback({
TmpSecretId: credentials.tmpSecretId,
TmpSecretKey: credentials.tmpSecretKey,
SecurityToken: credentials.sessionToken,
// 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
StartTime: data.startTime, // 时间戳,单位秒,如:1580000000
ExpiredTime: data.expiredTime, // 时间戳,单位秒,如:1580000000
})
}
xhr.send();
}
})
},
uploadToCOS() {
const bucket = ${bucket};
const region = ${region};
const key = `images/${Date.now()}_${this.imageFile.name}`;
this.cos.uploadFile(
{
Bucket: bucket,
Region: region,
Key: key,
StorageClass: "STANDARD",
Body: this.imageFile,
},
(err) => {
if (err) {
console.error("Error uploading image:", err);
alert("Error uploading image.");
} else {
this.imageUrl = `https://${bucket}.cos.${region}.myqcloud.com/${key}`;
console.log("Image uploaded successfully. URL:", this.imageUrl);
alert("Image uploaded successfully.");
this.$refs.imageInput.value = null;
this.imageFile = null;
}
}
);
},
},
};
</script>
点击对应按钮即可上传图片至 COS