feat: add support for Aliyun OSS uploader

This commit is contained in:
yanghao05
2025-04-07 15:15:51 +08:00
parent 753cfde9d5
commit 7308824af8
11 changed files with 252 additions and 0 deletions

View File

@@ -0,0 +1,56 @@
package ali
import (
"go.ipao.vip/atom/container"
"go.ipao.vip/atom/opt"
)
const DefaultPrefix = "Ali"
func DefaultProvider() container.ProviderContainer {
return container.ProviderContainer{
Provider: Provide,
Options: []opt.Option{
opt.Prefix(DefaultPrefix),
},
}
}
type Config struct {
AccessKeyId string
AccessKeySecret string
Bucket string
Region string
Host *string
CallbackURL string
}
func Provide(opts ...opt.Option) error {
o := opt.New(opts...)
var config Config
if err := o.UnmarshalConfig(&config); err != nil {
return err
}
return container.Container.Provide(func() (*Config, error) {
return &config, nil
}, o.DiOptions()...)
}
type PolicyToken struct {
Policy string `json:"policy"`
SecurityToken string `json:"security_token"`
SignatureVersion string `json:"x_oss_signature_version"`
Credential string `json:"x_oss_credential"`
Date string `json:"x_oss_date"`
Signature string `json:"signature"`
Host string `json:"host"`
Dir string `json:"dir"`
Callback string `json:"callback"`
}
type CallbackParam struct {
CallbackUrl string `json:"callbackUrl"`
CallbackBody string `json:"callbackBody"`
CallbackBodyType string `json:"callbackBodyType"`
}

View File

@@ -0,0 +1,123 @@
package ali
import (
"crypto/hmac"
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"hash"
"io"
"strings"
"time"
"github.com/aliyun/credentials-go/credentials"
"github.com/pkg/errors"
)
func (c *Config) GetToken(path string) (*PolicyToken, error) {
product := "oss"
host := fmt.Sprintf("https://%s.oss-%s.aliyuncs.com", c.Bucket, c.Region)
if c.Host != nil {
host = *c.Host
}
// 设置上传目录
dir := strings.TrimRight(path, "/") + "/"
// callbackUrl为 上传回调服务器的URL请将下面的IP和Port配置为您自己的真实信息。
config := new(credentials.Config).
SetType("access_key").
SetAccessKeyId(c.AccessKeyId).
SetAccessKeySecret(c.AccessKeySecret).
SetPolicy("")
// SetType("ram_role_arn").
// SetRoleArn(os.Getenv("OSS_STS_ROLE_ARN")).
// SetRoleSessionName("Role_Session_Name").
// SetRoleSessionExpiration(3600)
// 根据配置创建凭证提供器
provider, err := credentials.NewCredential(config)
if err != nil {
return nil, errors.Wrap(err, "NewCredential fail")
}
// 从凭证提供器获取凭证
cred, err := provider.GetCredential()
if err != nil {
return nil, errors.Wrap(err, "GetCredential fail")
}
// 构建policy
utcTime := time.Now().UTC()
date := utcTime.Format("20060102")
expiration := utcTime.Add(1 * time.Hour)
policyMap := map[string]any{
"expiration": expiration.Format("2006-01-02T15:04:05.000Z"),
"conditions": []any{
map[string]string{"bucket": c.Bucket},
map[string]string{"x-oss-signature-version": "OSS4-HMAC-SHA256"},
map[string]string{"x-oss-credential": fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request", *cred.AccessKeyId, date, c.Region, product)},
map[string]string{"x-oss-date": utcTime.Format("20060102T150405Z")},
map[string]string{"x-oss-security-token": *cred.SecurityToken},
},
}
// 将policy转换为 JSON 格式
policy, err := json.Marshal(policyMap)
if err != nil {
return nil, errors.Wrap(err, "json.Marshal fail")
}
// 构造待签名字符串StringToSign
stringToSign := base64.StdEncoding.EncodeToString([]byte(policy))
hmacHash := func() hash.Hash { return sha256.New() }
// 构建signing key
signingKey := "aliyun_v4" + *cred.AccessKeySecret
h1 := hmac.New(hmacHash, []byte(signingKey))
io.WriteString(h1, date)
h1Key := h1.Sum(nil)
h2 := hmac.New(hmacHash, h1Key)
io.WriteString(h2, c.Region)
h2Key := h2.Sum(nil)
h3 := hmac.New(hmacHash, h2Key)
io.WriteString(h3, product)
h3Key := h3.Sum(nil)
h4 := hmac.New(hmacHash, h3Key)
io.WriteString(h4, "aliyun_v4_request")
h4Key := h4.Sum(nil)
// 生成签名
h := hmac.New(hmacHash, h4Key)
io.WriteString(h, stringToSign)
signature := hex.EncodeToString(h.Sum(nil))
var callbackParam CallbackParam
callbackParam.CallbackUrl = c.CallbackURL
callbackParam.CallbackBody = "filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}"
callbackParam.CallbackBodyType = "application/x-www-form-urlencoded"
callback_str, err := json.Marshal(callbackParam)
if err != nil {
fmt.Println("callback json err:", err)
}
callbackBase64 := base64.StdEncoding.EncodeToString(callback_str)
// 构建返回给前端的表单
return &PolicyToken{
Policy: stringToSign,
SecurityToken: *cred.SecurityToken,
SignatureVersion: "OSS4-HMAC-SHA256",
Credential: fmt.Sprintf("%v/%v/%v/%v/aliyun_v4_request", *cred.AccessKeyId, date, c.Region, product),
Date: utcTime.UTC().Format("20060102T150405Z"),
Signature: signature,
Host: host, // 返回 OSS 上传地址
Dir: dir, // 返回上传目录
Callback: callbackBase64, // 返回上传回调参数
}, nil
}