You need to enable JavaScript to run this app.
文档中心
对象存储

对象存储

复制全文
下载 pdf
上传对象
上传回调(Go SDK)
复制全文
下载 pdf
上传回调(Go SDK)

上传回调是指客户端在请求时携带回调(Callback)参数,服务端在上传完成后,发送同步的 POST 回调请求到 CallBack 中指定的第三方应用服务器,在服务器确认接受并返回结果后,才将所有结果返回给客户端。
关于上传回调的详细介绍,请参见上传回调

示例代码

普通上传实现上传回调

package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "os"
    "strings"

    "github.com/volcengine/ve-tos-golang-sdk/v2/tos"
)

func checkErr(err error) {
    if err != nil {
       if serverErr, ok := err.(*tos.TosServerError); ok {
          fmt.Println("Error:", serverErr.Error())
          fmt.Println("Request ID:", serverErr.RequestID)
          fmt.Println("Response Status Code:", serverErr.StatusCode)
          fmt.Println("Response Header:", serverErr.Header)
          fmt.Println("Response Err Code:", serverErr.Code)
          fmt.Println("Response Err Msg:", serverErr.Message)
       } else if clientErr, ok := err.(*tos.TosClientError); ok {
          fmt.Println("Error:", clientErr.Error())
          fmt.Println("Client Cause Err:", clientErr.Cause.Error())
       } else {
          fmt.Println("Error:", err)
       }
       panic(err)
    }
}

func main() {
    var (
       // 火山引擎账号 accessKey 具有 TOS 访问权限
       ak = os.Getenv("TOS_ACCESS_KEY")
       sk = os.Getenv("TOS_SECRET_KEY")
       // Bucket 对于的 Endpoint,以华北2(北京)为例:https://tos-cn-beijing.volces.com
       endpoint = "https://tos-cn-beijing.volces.com"
       region   = "cn-beijing"
       // 填写 BucketName
       bucketName = "*** Provide your bucket name ***"
       // 填写对象名
       objectKey   = "*** Provide your object key ***"
       callbackUrl = "*** Provide your callback url ***"
    )
    // 初始化客户端
    client, err := tos.NewClientV2(endpoint, tos.WithRegion(region), tos.WithCredentials(tos.NewStaticCredentials(ak, sk)))
    checkErr(err)

    // 设置上传回掉参数
    // 可以根据需求设置 Callback 参数
    callback := `
    {
       "callbackUrl" : "` + callbackUrl + `", 
       "callbackBody" : "{\"bucket\": ${bucket}, \"object\": ${object}, \"key1\": ${x:key1}}", 
       "callbackBodyType" : "application/json"                
    }`
    // 配置相关变量参数
    callbackVar := `
    {
       "x:key1" : "ceshi"
    }`

    body := strings.NewReader("object content")
    output, err := client.PutObjectV2(context.Background(), &tos.PutObjectV2Input{
       PutObjectBasicInput: tos.PutObjectBasicInput{
          Bucket:      bucketName,
          Key:         objectKey,
          Callback:    base64.StdEncoding.EncodeToString([]byte(callback)),
          CallbackVar: base64.StdEncoding.EncodeToString([]byte(callbackVar)),
       },
       Content: body,
    })
    checkErr(err)
    fmt.Println("Put Object Request ID: ", output.RequestID)
    fmt.Println("Put Object Response Status Code: ", output.StatusCode)
}

分片上传实现上传回调

package main

import (
    "context"
    "encoding/base64"
    "fmt"
    "os"

    "github.com/volcengine/ve-tos-golang-sdk/v2/tos"
)

func checkErr(err error) {
    if err != nil {
       if serverErr, ok := err.(*tos.TosServerError); ok {
          fmt.Println("Error:", serverErr.Error())
          fmt.Println("Request ID:", serverErr.RequestID)
          fmt.Println("Response Status Code:", serverErr.StatusCode)
          fmt.Println("Response Header:", serverErr.Header)
          fmt.Println("Response Err Code:", serverErr.Code)
          fmt.Println("Response Err Msg:", serverErr.Message)
       } else if clientErr, ok := err.(*tos.TosClientError); ok {
          fmt.Println("Error:", clientErr.Error())
          fmt.Println("Client Cause Err:", clientErr.Cause.Error())
       } else {
          fmt.Println("Error:", err)
       }
       panic(err)
    }
}

func main() {
    var (
       accessKey = os.Getenv("TOS_ACCESS_KEY")
       secretKey = os.Getenv("TOS_SECRET_KEY")
       // Bucket 对应的 Endpoint,以华北2(北京)为例:https://tos-cn-beijing.volces.com
       endpoint = "https://tos-cn-beijing.volces.com"
       region   = "cn-beijing"
       // 填写 BucketName
       bucketName = "*** Provide your bucket name ***"

       // 指定的 ObjectKey
       objectKey = "*** Provide your object name ***"
       ctx       = context.Background()
       uploadId  = "*** Provide your upload id ***"
       // 需要合并的 parts
       parts = make([]tos.UploadedPartV2, 0)
       callbackUrl = "*** Provide your callback url ***"
    )
    // 初始化客户端
    client, err := tos.NewClientV2(endpoint, tos.WithRegion(region), tos.WithCredentials(tos.NewStaticCredentials(accessKey, secretKey)))
    checkErr(err)
    // 设置上传回掉参数
    // 可以根据需求设置 Callback 参数
    callback := `
    {
       "callbackUrl" : "` + callbackUrl + `", 
       "callbackBody" : "{\"bucket\": ${bucket}, \"object\": ${object}, \"key1\": ${x:key1}}", 
       "callbackBodyType" : "application/json"                
    }`
    // 配置相关变量参数
    callbackVar := `
    {
       "x:key1" : "ceshi"
    }`

    completeOutput, err := client.CompleteMultipartUploadV2(ctx, &tos.CompleteMultipartUploadV2Input{
       Bucket:      bucketName,
       Key:         objectKey,
       UploadID:    uploadId,
       Parts:       parts,
       Callback:    base64.StdEncoding.EncodeToString([]byte(callback)),
       CallbackVar: base64.StdEncoding.EncodeToString([]byte(callbackVar)),
    })
    checkErr(err)
    fmt.Println("CompleteMultipartUploadV2 Request ID:", completeOutput.RequestID)

}

验证回调签名的示例代码

package main

import (
    "bytes"
    "crypto"
    "crypto/md5"
    "crypto/rsa"
    "crypto/x509"
    "encoding/base64"
    "encoding/pem"
    "errors"
    "fmt"
    "io/ioutil"
    "net/http"
    "sort"
)

const Authorization = "Authorization"
const XTosPublicKeyUrl = "x-tos-pub-key-url"

func calcStringToSign(req *http.Request, body []byte) []byte {
    buf := bytes.NewBuffer(nil)
    buf.WriteString(req.URL.Path)

    query := req.URL.Query()
    if len(query) > 0 {
       buf.WriteByte('?')
       keys := make([]string, 0, len(query))
       for key := range query {
          keys = append(keys, key)
       }
       sort.Strings(keys)

       for _, key := range keys {
          values := query[key]
          for _, value := range values {
             buf.WriteString(key)
             buf.WriteByte('=')
             buf.WriteString(value)
             buf.WriteByte('&')
          }
       }

       buf.Truncate(buf.Len() - 1)
    }

    buf.WriteByte('\n')
    buf.Write(body)
    return buf.Bytes()
}

func getSignature(r *http.Request) ([]byte, error) {
    authorization := r.Header.Get(Authorization)
    if authorization == "" {
       return nil, nil
    }

    return base64.StdEncoding.DecodeString(authorization)
}

func getPublicKey(r *http.Request) ([]byte, error) {
    var bytePublicKey []byte

    publicKeyURLBase64 := r.Header.Get(XTosPublicKeyUrl)
    if publicKeyURLBase64 == "" {
       return bytePublicKey, errors.New("no x-tos-pub-key-url field in request header ")
    }
    publicKeyURL, _ := base64.StdEncoding.DecodeString(publicKeyURLBase64)

    resp, err := http.Get(string(publicKeyURL))
    if err != nil {
       return bytePublicKey, err
    }

    bytePublicKey, err = ioutil.ReadAll(resp.Body)
    defer resp.Body.Close()
    if err != nil {
       return bytePublicKey, err
    }

    if resp.StatusCode >= http.StatusBadRequest {
       return nil, errors.New("get public key failed")
    }

    return bytePublicKey, nil
}

func getContentAndSignMD5(r *http.Request) ([]byte, []byte, error) {
    bodyContent, err := ioutil.ReadAll(r.Body)
    _ = r.Body.Close()
    if err != nil {
       return nil, nil, err
    }

    stringToSign := calcStringToSign(r, bodyContent)

    authMd5 := md5.New()
    authMd5.Write(stringToSign)
    signMd5 := authMd5.Sum(nil)
    return signMd5, bodyContent, nil
}

func verifySignature(publicKey []byte, signMd5 []byte, sign []byte) error {
    pubBlock, _ := pem.Decode(publicKey)
    if pubBlock == nil {
       return errors.New("invalid public key pem")
    }
    pubInterface, err := x509.ParsePKIXPublicKey(pubBlock.Bytes)
    if err != nil || pubInterface == nil {
       return errors.New("invalid public key pem")
    }

    pub := pubInterface.(*rsa.PublicKey)

    err = rsa.VerifyPKCS1v15(pub, crypto.MD5, signMd5, sign)
    if err != nil {
       return err
    }
    return nil
}

func CallbackHandler(w http.ResponseWriter, r *http.Request) {
    // 获取签名
    sign, err := getSignature(r)
    if err != nil {
       fmt.Println("get callback signature err:", err.Error())
       w.WriteHeader(http.StatusBadRequest)
       return
    }

    // 获取公共签名,该结果可以缓存
    publicKey, err := getPublicKey(r)
    if err != nil {
       w.WriteHeader(http.StatusBadRequest)
       return
    }

    // 读取 body 并计算签名
    signMd5, body, err := getContentAndSignMD5(r)
    if err != nil {
       w.WriteHeader(http.StatusBadRequest)
       return
    }

    // 验证签名
    if err = verifySignature(publicKey, signMd5, sign); err != nil {
       w.WriteHeader(http.StatusUnauthorized)
       return
    }

    // 处理回调 body
    fmt.Println(body)
    // 模拟返回
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"msg":"ok"}`))
    return
}
最近更新时间:2024.04.22 21:26:58
这个页面对您有帮助吗?
有用
有用
无用
无用