首先吐槽一下又拍云的文档,需要有亿点改进。
由于我是前端上传,官网没有js sdk,所以需要结合后端来生成签名和policy。
前端使用的是form api的方式上传。
后端
直接上后端代码(golang):
package application
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"fmt"
"strings"
"time"
)
type Upyun struct {
Token string `json:"token"`
Date string `json:"date"`
Policy string `json:"policy"`
}
func makeSaveKey() string {
return "/{year}/{mon}/{day}/upload_{random32}{.suffix}"
}
func makePolicy(bucket, saveKey string) string {
// 过期时间设置为一天。
expiration := time.Now().AddDate(0, 0, 1).Unix()
obj := struct {
Bucket string `json:"bucket"`
SaveKey string `json:"save-key"`
Expiration int64 `json:"expiration"`
// Date string `json:"date"`
}{
Bucket: bucket,
SaveKey: saveKey,
Expiration: expiration,
// Date: date,
}
str, err := json.Marshal(&obj)
if err != nil {
return ""
}
sEnc := base64.StdEncoding.EncodeToString([]byte(str))
return sEnc
}
func md5Str(s string) string {
return fmt.Sprintf("%x", md5.Sum([]byte(s)))
}
func makeRFC1123Date(d time.Time) string {
utc := d.UTC().Format(time.RFC1123)
return strings.Replace(utc, "UTC", "GMT", -1)
}
func base64ToStr(b []byte) string {
return base64.StdEncoding.EncodeToString(b)
}
func sign(key, secret, method, uri, date, policy, md5 string) string {
mac := hmac.New(sha1.New, []byte(secret))
elems := []string{}
for _, v := range []string{method, uri, date, policy, md5} {
if v != "" {
elems = append(elems, v)
}
}
value := strings.Join(elems, "&")
mac.Write([]byte(value))
signStr := base64ToStr(mac.Sum(nil))
return "UPYUN " + key + ":" + signStr
}
func GetUpyunToken(key, secret, bucket, method string) *Upyun {
date := makeRFC1123Date(time.Now())
saveKey := makeSaveKey()
policy := makePolicy(bucket, saveKey)
token := sign(key, md5Str(secret), method, "/"+bucket, "", policy, "")
upyun := Upyun{
Date: date,
Token: token,
Policy: policy,
}
return &upyun
}
说说遇到的坑,
-
签名和policy中不要设置date数据,不然前端上传时的时间与签名和policy中的时间对不上。
-
调用sign方法时method参数后一个参数在拼接时需要加上/,不然签名不对,比如下面这样。
method&/uri&policy
上面的问题是在客服技术人员的耐心指导下调通的。在此感谢技术大佬。
前端
前端使用的antd的upload组件,
<Upload
name="file"
method={'POST'}
listType="picture-card"
className="avatar-uploader"
showUploadList={false}
action={`https://v0.api.upyun.com/${bucketName}`}
beforeUpload={beforeUpload}
onChange={handleChange}
data={{
authorization: xxxx,
policy: xxx,
}}
>
{value ? <img src={value} alt="avatar" style={{width: '100%'}} /> : uploadButton}
</Upload>
由于签名需要涉及到密码,放在服务器上生成比较安全,前端使用即可。
原文地址