上传回调是指客户端在请求时携带回调(Callback)参数,服务端在上传完成后,发送同步的 POST 回调请求到 CallBack 中指定的第三方应用服务器,在服务器确认接受并返回结果后,才将所有结果返回给客户端。由于加入了回调请求和等待响应的过程,相比于普通上传会有更多的等待时间。
上传回调说明
目前支持上传回调的接口如下:
上传回调的流程如下:

步骤一:构造回调参数
使用上传回调依赖客户端在请求时携带回调参数和回调参数变量。
回调参数
回调参数是一段经过 Base64 编码的 JSON 格式字符串。示例如下:
// 示例 1,传递 application/json 类型的消息体
{
"callbackUrl" : "http://domainname.com/callback",
"callbackHost" : "alternative-domainname.com",
"callbackBody" : "{\"bucket\" : ${bucket}, \"object\" : ${object}, \"key1\" : ${x:key1}, \"key2\" : ${x:key2}}",
"callbackBodyType" : "application/json"
}
// 示例 2,传递 application/x-www-form-urlencoded 类型的消息体
{
"callbackUrl" : "http://domain-name/callback",
"callbackHost" : "alternative-domain-name",
"callbackBody" : "bucket=${bucket}&object=${object}&key1=${x:key1}&key2=${x:key2}",
"callbackBodyType" : "application/x-www-form-urlencoded"
}
回调参数说明如下:
字段 | 作用描述 | 约束限制 |
|---|
callbackUrl | 回调请求的 URL,用于指定回调请求的第三方应用服务的地址,TOS 服务在上传成功后会向该地址发送 HTTP/HTTPS 请求。 | - 回调参数可为空,为空时代表本次请求没有设置上传回调参数。
- URL 格式为 HTTP/HTTPS 协议地址,不指定协议类型时,默认为
https://。 - URL 中的 path 和 query 部分必须经过 URL 编码。
- 支持最多 5 个 URL,不同 URL 之间用分号
; 分隔。TOS 服务端在回调请求时会按顺序依次请求,直至第一个返回成功的 URL 为止。 - 不支持回调参数变量。
|
callbackHost | 回调请求发送时的 HOST 头域,用于替换 callbackUrl 中解析出的 HOST。 | - 可为空,不为空时必须符合域名或 IP 的格式规则。
- 不支持回调参数变量。
|
callbackBody | 回调请求发送时的消息体,格式需与 callbackBodyType 指定的类型匹配。 | - 不可为空且必须为合法的格式,TOS 服务端会强校验该字段。
- 支持回调参数变量。
|
callbackBodyType | 回调请求的消息体类型(Content-Type),与 callbackBody 配套使用。 | - 可为空,为空时默认为 application/x-www-form-urlencoded。
- 只支持 application/x-www-form-urlencoded 和 application/json 两种类型的值,其他值将被视为不合法。
- 不支持回调参数变量。
|
注意
- 当 callbackUrl 为空时,表示本次请求没有设置上传回调参数,此时其它参数无效。
- callbackUrl 和 callbackHost 包含的域名或 IP 不能是特殊的域名和 IP,例如 127.0.0.1、0.0.0.0、0:0:0:0:0:0:0:1、0:0:0:0:0:0:0:0 及 localhost。
回调参数变量
回调参数中的 callbackBody 支持使用回调参数变量,分为系统内置变量和自定义变量(callback-var)两类,前者可以在 callbackBody 中直接使用,后者需要客户端在请求时一并携带。TOS 服务端会根据 JSON 格式解析出自定义变量并与系统内置变量合并为一组可用的键值对,用于 callbackBody 的中 ${variable_name} 和 ${x:variable_name} 形式变量的替换。
系统内置变量
callbackBody 中可以设置的系统内置变量请参见下表。
注意
- 在 callbackBody 中使用系统内置变量时,必须添加
${} 操作符,例如 ${bucket} 代表获取上传的桶名。 - 当 callbackBodyType 为 application/json 时,作为 JSON 格式中的字段值会按照变量类型填充。例如 bucket 为字符串 bucket-test,size 为整型 1024,则 {"bucket": ${bucket}, "size" : ${size}} 解析后的值为 {"bucket" : "bucket-test", "size" : 1024}。
变量名 | 变量说明 | 变量类型 |
|---|
bucket | 上传的桶名。 | string |
key | 上传的对象名。 | string |
object | 上传的对象名。 | string |
size | 上传对象的大小。 | int64 |
etag | 上传后对象的 ETag。 | string |
crc64ecma | 上传后对象的 CRC64。 | string |
versionId | 上传后对象的版本号,只在桶开启了多版本特性时有值。 | string |
filename | PostObject 上传的文件原名。 | string |
fname | PostObject 上传的文件原名。 | string |
mimeType | 上传对象时客户端指定的 Content-Type,客户端未指定时默认为 binary/octet-stream。 | string |
requestId | 本次请求的 Request ID。 | string |
自定义变量
客户端在请求时可携带 JSON 格式的自定义变量键值对,TOS 服务端会根据 JSON 格式解析出自定义变量用于 callbackBody 中变量的替换。如下所示,定义了四个自定义变量:
{
"x:key1" : "value1", // 定义字符串类型的字段
"x:key2" : 123, // 定义数字类型的字段
"x:key3" : ["value2", "value3"], // 定义数组类型的字段
"x:key4" : true // 定义布尔类型的字段
}
注意
- 自定义变量必须以
x: 开头,否则视为不合法。 - 在 callbackBody 中使用自定义变量时,必须添加 ${} 操作符,例如 ${x:key1} 代表获取变量 x:key1。
- 当 callbackBodyType 为 application/json 时,TOS 服务端的填充规则与使用系统内置变量时一致。
- 当 callbackBodyType 为 application/x-www-form-urlencoded 时,callbackBody 中的系统变量(key、object、fname、filename)会在填充时被 url_encode 编码。
步骤二:构造回调请求
构造方式
上传回调支持的接口包括 PutObject、PostObject、CompleteMultipartUpload,其中 PutObject 及 CompleteMultipartUpload 可以使用 URL 或 Header 携带回调参数和自定义变量;PostObject 使用表单域携带回调参数和自定义变量。客户端构造上传回调请求的方式如下所示。
上传回调支持接口 | 支持的参数携带方式 | 参数定义 | 签名方式 | 约束限制 |
|---|
PutObject、CompleteMultipartUpload | Header 中携带参数 | Header 使用: - x-tos-callback
- x-tos-callback-var
| 按照签名机制计算到 CanonicalHeaders 和 SignedHeaders 中。 | - 两个变量都必须是经过 Base64 编码的 JSON 格式。
- 不能在 Header 和 Query 上重复出现回调参数和自定义变量,但可以各包含一个实现组合。
|
URL 中携带参数 | Query 使用: - x-tos-callback
- x-tos-callback-var
| 按照签名机制计算到 CanonicalQueryString 中。 |
PostObject | 使用 POST 表单域携带 | - 表单域使用 x-tos-callback
- 表单域使用 x-tos-callback-var 或通过表单域传递自定义变量各字段。
| 按照 POST 上传对象时的签名机制将 x-tos-callback、x-tos-callback-var 和自定义变量放入表单上传的 policy 中做为 conditions。 | - x-tos-callback 和 x-tos-callback-var 必须是经过 Base64 编码的 JSON 格式。
- 优先使用 x-tos-callback-var,如果没有携带 x-tos-callback-var,则代表自定义变量的每个字段是通过表单域来传递的。
|
参数说明
构造示例
本文以第三方应用服务器对于 TOS 的回调请求返回响应 {"Status":"OK"}为例,展示如何构造回调请求。
构造回调参数:
// 回调参数:
{
"callbackUrl" : "http://domainname.com/callback",
"callbackHost" : "alternative-domainname.com",
"callbackBody" : "{\"bucket\" : ${bucket}, \"object\" : ${object}, \"key1\" : ${x:key1}, \"key2\" : ${x:key2}}",
"callbackBodyType" : "application/json"
}
// 回调参数变量:
{
"x:key1" : "value1",
"x:key2" : 123,
}
// 第三方应用服务返回的结果:
{"Status":"OK"}
对回调参数和回调参数变量进行 Base64 编码。
// 对回调参数进行 Base64 Encode 得到 x-tos-callback:
Cgl7CgkJImNhbGxiYWNrVXJsIiA6ICJodHRwOi8vZG9tYWlubmFtZS5jb20vY2FsbGJhY2siLCAKCQkiY2FsbGJhY2tIb3N0IiA6ICJhbHRlcm5hdGl2ZS1kb21haW5uYW1lLmNvbSIsICAgICAgICAgICAgICAgCgkJImNhbGxiYWNrQm9keSIgOiAie1wiYnVja2V0XCIgOiAke2J1Y2tldH0sIFwib2JqZWN0XCIgOiAke29iamVjdH0sIFwia2V5MVwiIDogJHt4OmtleTF9LCBcImtleTJcIiA6ICR7eDprZXkyfX0iLCAKCQkiY2FsbGJhY2tCb2R5VHlwZSIgOiAiYXBwbGljYXRpb24vanNvbiIgICAgICAgICAgICAgICAgCgl9
// 对回调参数变量进行 Base64 Encode 得到 x-tos-callback-var:
ewogICAgIng6a2V5MSIgOiAidmFsdWUxIiwKICAgICJ4OmtleTIiIDogMTIzLAp9
构造上传回调请求。
构造 PutObject 请求并在 Header 中携带回调参数和自定义变量,内容如下:
PUT /objectName HTTP/1.1
Host: bucketname.tos-cn-beijing.volces.com
Date: Fri, 30 Jul 2021 08:05:36 +0000
x-tos-callback-var: ewogICAgIng6a2V5MSIgOiAidmFsdWUxIiwKICAgICJ4OmtleTIiIDogMTIzLAp9
x-tos-callback: Cgl7CgkJImNhbGxiYWNrVXJsIiA6ICJodHRwOi8vZG9tYWlubmFtZS5jb20vY2FsbGJhY2siLCAKCQkiY2FsbGJhY2tIb3N0IiA6ICJhbHRlcm5hdGl2ZS1kb21haW5uYW1lLmNvbSIsICAgICAgICAgICAgICAgCgkJImNhbGxiYWNrQm9keSIgOiAie1wiYnVja2V0XCIgOiAke2J1Y2tldH0sIFwib2JqZWN0XCIgOiAke29iamVjdH0sIFwia2V5MVwiIDogJHt4OmtleTF9LCBcImtleTJcIiA6ICR7eDprZXkyfX0iLCAKCQkiY2FsbGJhY2tCb2R5VHlwZSIgOiAiYXBwbGljYXRpb24vanNvbiIgICAgICAgICAgICAgICAgCgl9
Authorization: authorization_string
Content-Type: text/plain
Content-Length: 0
构造 CompleteMultipartUpload 请求并在 URL 中携带回调参数和自定义变量,内容如下:
POST /objectName?uploadId=UploadId&x-tos-callback=Cgl7CgkJImNhbGxiYWNrVXJsIiA6ICJodHRwOi8vZG9tYWlubmFtZS5jb20vY2FsbGJhY2siLCAKCQkiY2FsbGJhY2tIb3N0IiA6ICJhbHRlcm5hdGl2ZS1kb21haW5uYW1lLmNvbSIsICAgICAgICAgICAgICAgCgkJImNhbGxiYWNrQm9keSIgOiAie1wiYnVja2V0XCIgOiAke2J1Y2tldH0sIFwib2JqZWN0XCIgOiAke29iamVjdH0sIFwia2V5MVwiIDogJHt4OmtleTF9LCBcImtleTJcIiA6ICR7eDprZXkyfX0iLCAKCQkiY2FsbGJhY2tCb2R5VHlwZSIgOiAiYXBwbGljYXRpb24vanNvbiIgICAgICAgICAgICAgICAgCgl9&x-tos-callback-var=ewogICAgIng6a2V5MSIgOiAidmFsdWUxIiwKICAgICJ4OmtleTIiIDogMTIzLAp9 HTTP/1.1
Host: bucketname.tos-cn-beijing.volces.com
Date: Fri, 30 Jul 2021 08:05:36 +0000
Authorization: authorization_string
{
"Parts":[{
"PartNumber":1,
"ETag":"7de47c292287f7965357c1ddd724b2b7"
}]
}
构造 PostObject 请求并在表单域携带回调参数和自定义变量,内容如下:
POST / HTTP/1.1
Host: buketname.tos-cn-beijing.volces.com
Content-Type: multipart/form-data; boundary=9431149156168
Content-Length: length
--9431149156168
Content-Disposition: form-data; name="key"
objectname
--9431149156168
Content-Disposition: form-data; name="x-tos-callback"
Cgl7CgkJImNhbGxiYWNrVXJsIiA6ICJodHRwOi8vZG9tYWlubmFtZS5jb20vY2FsbGJhY2siLCAKCQkiY2FsbGJhY2tIb3N0IiA6ICJhbHRlcm5hdGl2ZS1kb21haW5uYW1lLmNvbSIsICAgICAgICAgICAgICAgCgkJImNhbGxiYWNrQm9keSIgOiAie1wiYnVja2V0XCIgOiAke2J1Y2tldH0sIFwib2JqZWN0XCIgOiAke29iamVjdH0sIFwia2V5MVwiIDogJHt4OmtleTF9LCBcImtleTJcIiA6ICR7eDprZXkyfX0iLCAKCQkiY2FsbGJhY2tCb2R5VHlwZSIgOiAiYXBwbGljYXRpb24vanNvbiIgICAgICAgICAgICAgICAgCgl9
--9431149156168
Content-Disposition: form-data; name="x:key1"
value1
--9431149156168
Content-Disposition: form-data; name="x:key2"
123
--9431149156168
Content-Disposition: form-data; name="x-tos-algorithm"
TOS4-HMAC-SHA256
--9431149156168
Content-Disposition: form-data; name="x-tos-date"
20220411T000000Z
--9431149156168
Content-Disposition: form-data; name="x-tos-credential"
<your-access-key-id>/<date>/<region>/<service>/request
--9431149156168
Content-Disposition: form-data; name="policy"
base64encoded_policy
--9431149156168
Content-Disposition: form-data; name="x-tos-signature"
signature
--9431149156168
Content-Disposition: form-data; name="file"; filename="MyFilename.jpg"
Content-Type: image/jpeg
file_content
--9431149156168
Content-Disposition: form-data; name="submit"
Upload to TOS
--9431149156168--
步骤三:发起回调请求
TOS 服务端完成对象上传后,会根据上传请求中的回调参数配置发送 Post 请求调用第三方应用服务,第三方应用服务器实现的回调接口应满足以下约束。
约束项 | 要求 |
|---|
接口协议 | HTTP、HTTPS,使用 HTTPS 协议时第三方应用服务必须有合法的证书。 |
支持方法 | POST |
请求消息体格式 | application/x-www-form-urlencoded、application/json |
响应状态码 | 200 |
响应消息体格式 | application/json |
响应消息体大小 | 不超过 3MB。 |
TOS 根据用户请求中的回调参数和回调参数变量,使用 Post 请求调用第三方服务器,示例如下:
POST /callback HTTP/1.1
Host: alternative-domainname.com
Content-Length: 71
Content-Type: application/json
{"bucket":"bucket-test","object":"key-test","key1":"value1","key2":123}
(可选)步骤四:回调签名
第三方应用服务器收到回调请求后,可以通过回调中的签名来验证请求是否来自 TOS。
TOS 服务端采用 RSA 非对称方式自动计算签名,并发送给第三方应用服务器。以下介绍 TOS 服务端生成签名的过程。
创建待签名字符串(StringToSign)。
url_decode(path) + '?' + url_decode(sorted(query)) + '\n' + body
使用 MD5 哈希函数计算待签名字符串的哈希值。
md5(StringToSign)
使用私钥对待签名字符串的哈希值进行加密,生成签名。
rsa_sign(private_key, url_decode(path) + '?' + url_decode(sorted(query)) + '\n' + body, md5)
对签名进行 Base64 编码,然后将编码后的签名添加到回调请求头 Authorization 中,并发送给第三方应用服务器。
Authorization = base64_encode(rsa_sign(private_key, url_decode(path) + '?' + url_decode(sorted(query)) + '\n' + body, md5))
以下介绍第三方服务器的验签过程。
回调请求头 x-tos-pub-key-url 的值是经过 Base64 编码的签名公钥的下载地址,使用 Base64 解码 x-tos-pub-key-url的值,然后将公钥下载到本地。
说明
- 为了保证该公钥是由 TOS 颁发的,您可以校验公钥的下载地址,公钥的下载地址必须以
http://tos-public.volccdn.com/ 或者 https://tos-public.volccdn.com/ 开头。 - 公钥是长期不变的,建议您将公钥存储在本地的缓存中,以便后续使用。
对回调请求头 Authorization 的值进行 Base64 解码,获取签名。
通过请求中获取到的请求路径、请求参数、请求消息体,创建待签名的字符串(StringToSign)。
url_decode(path) + '?' + url_decode(sorted(query)) + '\n' + body
注意
- 创建待签名字符串时,
query需要按照字典序进行排序。 - 如果
query 中只包含 key ,则您需要按照“key=”的格式创建待签名字符串。
使用 MD5 哈希函数计算待签名字符串的哈希值。
md5(StringToSign)
使用公钥、签名和待签名字符串的哈希值验证签名。
result = rsa_verify(public_key, md5(StringToSign), signature)
以 Go 为例,展示第三方应用服务器验证签名的方法。
package callback
import (
"bytes"
"crypto"
"crypto/md5"
"crypto/rsa"
"crypto/x509"
"encoding/base64"
"encoding/json"
"encoding/pem"
"errors"
log "github.com/sirupsen/logrus"
"io/ioutil"
"net/http"
"sort"
jsoniter "github.com/json-iterator/go"
)
const XTosPublicKeyUrl = "x-tos-pub-key-url"
const Authorization = "Authorization"
type CallbackAPI struct {
}
type ErrResp struct {
Err string `json:"Err"`
RetMsg string `json:"RetMsg"`
}
func (api *CallbackAPI) CallbackHandler(w http.ResponseWriter, r *http.Request) {
sign, err := getSignature(r)
if err != nil {
log.Warn("authorization: %s is not base64 encoding", r.Header.Get(Authorization))
JsonResponse(w, 401, ErrResp{Err: "[Callback] invalid Authorization, err:" + err.Error()})
return
}
publicKey, err := getPublicKey(r)
if err != nil {
JsonResponse(w, 401, ErrResp{Err: "[Callback] invalid public key, err:" + err.Error()})
return
}
signMd5, body, err := getContentAndSignMD5(r)
if err != nil {
JsonResponse(w, 401, ErrResp{Err: "[Callback] invalid public key, err:" + err.Error()})
return
}
if err = verifySignature(publicKey, signMd5, sign); err != nil {
JsonResponse(w, 401, ErrResp{Err: "[Callback] verify signature failed, err:" + err.Error()})
return
}
log.Info("[Callback] handle callback request")
bodyType := r.Header.Get("Content-Type")
switch bodyType {
case "application/json":
if !jsoniter.Valid(body) {
JsonResponse(w, 500, ErrResp{Err: "[Callback] the body is not a valid json"})
return
}
JsonResponse(w, 200, body)
default: // application/x-www-form-urlencoded
JsonResponse(w, 200, struct {
EncodedBody string `json:"encoded_body"`
}{
EncodedBody: base64.StdEncoding.EncodeToString(body),
})
}
}
func verifySignature(publicKey []byte, signMd5 []byte, sign []byte) error {
pubBlock, _ := pem.Decode(publicKey)
if pubBlock == nil {
log.Warn("Failed to parse PEM block containing the public key")
return errors.New("invalid public key pem")
}
pubInterface, err := x509.ParsePKIXPublicKey(pubBlock.Bytes)
if err != nil || pubInterface == nil {
log.Warn("x509.ParsePKIXPublicKey(publicKey) failed, err:%v", err)
return errors.New("invalid public key pem")
}
pub := pubInterface.(*rsa.PublicKey)
err = rsa.VerifyPKCS1v15(pub, crypto.MD5, signMd5, sign)
if err != nil {
log.Warn("verify signature failed, err:%v", err)
return err
}
return nil
}
func getContentAndSignMD5(r *http.Request) ([]byte, []byte, error) {
bodyContent, err := ioutil.ReadAll(r.Body)
_ = r.Body.Close()
if err != nil {
log.Warn("read request body failed, err:%v", err)
return nil, nil, err
}
stringToSign := calcStringToSign(r, bodyContent)
log.Info("stringToSign: %s", stringToSign)
authMd5 := md5.New()
authMd5.Write(stringToSign)
signMd5 := authMd5.Sum(nil)
return signMd5, bodyContent, nil
}
func getPublicKey(r *http.Request) ([]byte, error) {
var bytePublicKey []byte
publicKeyURLBase64 := r.Header.Get(XTosPublicKeyUrl)
if publicKeyURLBase64 == "" {
log.Warn("GetPublicKey from Request header failed : No x-tos-pub-key-url field. ")
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)) // ignore_security_alert
if err != nil {
log.Warn("Get PublicKey Content from URL failed : %s", err.Error())
return bytePublicKey, err
}
bytePublicKey, err = ioutil.ReadAll(resp.Body)
defer resp.Body.Close()
if err != nil {
log.Warn("Read PublicKey Content from URL failed : %s", err.Error())
return bytePublicKey, err
}
if resp.StatusCode >= http.StatusBadRequest {
log.Warn("get public key failed, res:%s", string(bytePublicKey))
return nil, errors.New("get public key failed")
}
return bytePublicKey, nil
}
func getSignature(r *http.Request) ([]byte, error) {
authorization := r.Header.Get(Authorization)
if authorization == "" {
return nil, nil
}
return base64.StdEncoding.DecodeString(authorization)
}
// url_decode(path) + '?' + url_decode(sorted(query)) + '\n' + body
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 JsonResponse(w http.ResponseWriter, status int, entity interface{}) {
// content-type 要在 WriteHeader 之前设置,否则不生效
if entity != nil {
w.Header().Set("Content-Type", "application/json")
}
w.WriteHeader(status)
if entity != nil {
if body, ok := entity.([]byte); ok {
_, _ = w.Write(body)
return
}
body, _ := json.Marshal(entity)
_, _ = w.Write(body)
}
}
步骤五:返回回调结果
第三方应用服务器返回响应给 TOS,示例如下:
HTTP/1.1 200 OK
Date: Fri, 1 Jul 2022 01:00:36 GMT
Content-Length: 15
Content-Type: application/json
{"Status":"OK"}
步骤六:返回上传结果
TOS 将第三方应用服务器返回的内容返回给用户。
HTTP/1.1 200 OK
x-tos-id-2: 367be10900210004-a444ed0
x-tos-request-id: 367be10900210004-a444ed0
Date: Fri, 30 Jul 2021 08:05:36 GMT
server: TosServer
Content-Length: 15
Content-Type: application/json
ETag: 1c06e540e11d65a51aeb724e72fa641a
{"Status":"OK"}
注意
- CompleteMultipartUpload 返回结果本身中存在消息体,使用上传回调功能后会覆盖原有的消息体,客户端需要对这种情况做好判断处理。
- 当使用上传回调功能时,会新增 Location 头域用于返回新创建对象的带域名的 URL,以及 ETag 头域用于返回 ETag 值。
HTTP/1.1 200 OK
x-tos-id-2: 9b84750a5bbc0029-a444ed0
x-tos-request-id: 9b84750a5bbc0029-a444ed0
Date: Fri, 30 Jul 2021 13:59:38 GMT
server: TosServer
Content-Length: 15
Content-Type: application/json
Location: http://bucketname.tos-cn-beijing.volces.com/objectname
ETag: 7de47c292287f7965357c1ddd724b2b7-1
{"Status":"OK"}
注意
- PostObject 使用上传回调功能后会返回消息体,因此 HTTP 状态码会从 204 转为 200,客户端需要对这种情况做好判断处理。
- 当出现 CallbackFailed 错误码时,对应的 HTTP 状态码为 203,客户端需要对这种情况做好判断处理,其他状态码具体信息,请参见响应码。
HTTP/1.1 200 OK
x-tos-id-2: 367be10900210004-a444ed0
x-tos-request-id: 367be10900210004-a444ed0
Date: Fri, 1 Jul 2022 01:00:36 GMT
Server: TosServer
Content-Length: 15
Content-Type: application/json
ETag: ab7abb0da4bca5323ab6119bb5dcd296
Location: http://bucketname.tos-cn-beijing.volces.com/objectname
{"Status":"OK"}
错误码
HTTP 状态码 | 错误码 | 错误信息 |
|---|
203 | CallbackFailed | 上传对象成功,但是回调请求第三方应用服务失败,例如解析域名错误、请求超时、响应内容不合法。 |
400 | InvalidCallbackArgument | - 回调参数(callback)中的字段校验不合法
- 自定义变量(callback-var)格式不合法
- Header 或 Query 包含重复的自定义参数、自定义变量。
|
403 | AccessDenied | PostObject 的 policy 校验回调参数、自定义变量未通过。 |