## 簡單反饋
如果資源上傳成功,服務端會響應HTTP 200返回碼,且在響應內容中包含兩個字段:
* `hash`:已上傳資源的校驗碼,供用戶核對使用。
* `key`:目標資源的最終名字,可由七牛云存儲自動命名;
以下是一個典型的上傳成功反饋:
~~~
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
{
"hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
"key": "gogopher.jpg",
}
~~~
如果資源上傳失敗,服務端會反饋相應的錯誤信息。比如,HTTP 401代表驗證失敗。此時相應內容中會包含詳細錯誤信息。錯誤信息同樣為JSON格式:{“error”:””}
以下是一個典型的上傳失敗反饋:
~~~
HTTP/1.1 400 Bad Request
Date: Mon, 05 Aug 2013 13:56:34 GMT
Server: nginx/1.0.14
Content-Type: application/json
Access-Control-Allow-Origin: *
Content-Length: 28
X-Log: MC;SBD:10;RBD:11;BDT:12;FOPD/400;FOPG:63/400;IO:109/400
X-Reqid: -RIAAIAI8UjcgRcT
X-Via: 1.1 jssq179:8080 (Cdn Cache Server V2.0), 1.1 jsyc96:9080 (Cdn Cache Server V2.0)
Connection: close
{
"error":"invalid argument"
}
~~~
這些返回的錯誤信息可以幫助開發者分析問題原因。完整的返回碼信息請參見[返回碼](http://developer.qiniu.com/docs/v6/api/reference/codes.html)。
從上面的錯誤示例中可以看到,響應頭中還包含了一些以`X-`為前綴的擴展字段,如`X-Reqid`和`X-Log`等。這些擴展信息非常有助于問題定位。我們建議開發者將所有接收到的錯誤信息寫到日志中,以便于我們的技術支持人員在協助分析問題時有足夠詳細的線索。
關于這些擴展字段的詳細描述,請參見[HTTP擴展字段](http://developer.qiniu.com/docs/v6/api/reference/extended-headers.html)。
## 回調(callback)
開發者可以要求七牛云存儲在某文件上傳完成后向特定的URL發起一個回調請求。七牛云存儲會將該回調的響應內容作為文件上傳響應的一部分一并返回給客戶端。回調的流程如下圖所示:

要啟用回調功能,業務服務器在簽發上傳憑證時需要設置[上傳策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)中的`callbackUrl`字段,并且設置`callbackBody`字段。
### 回調地址
通過設定上傳策略中的`callbackUrl`字段為一個有效的地址,即可讓七牛云存儲在文件上傳完成后向該地址發起回調請求。
該地址可以是一個HTTP或者HTTPS的URL,允許公網訪問。
如果需要傳遞自定義的請求內容,開發者可以考慮配合使用上傳策略中的`callbackBody`字段。如果只有`callbackUrl`而沒有`callbackBody`,回調服務器收到的請求內容將為空。
### 回調內容
同普通客戶端直傳和重定向上傳一樣,用戶也可以控制回調中傳遞到客戶回調服務器的反饋信息。`callbackBody`的格式如下:
~~~
<item>=(<magicvar>|<xvar>)[&<item>=(<magicvar>|<xvar>)...]
~~~
一個典型的`callbackBody`設置如下:
~~~
put_policy = '{
...
"callbackBody" : "name=$(fname)&hash=$(etag)&location=$(x:location)\
&price=$(x:price)&uid=123"
...
}'
~~~
上面的?`callbackBody`?示例中,混合使用了魔法變量(name,hash)、自定義變量(location,price)及自定義常量(uid)。 假設應用客戶端發出了如下的上傳請求:
~~~
<form method="post" action="http://upload.qiniu.com/" enctype="multipart/form-data">
<input name="key" type="hidden" value="sunflower.jpg">
<input name="x:location" value="Shanghai">
<input name="x:price" value="1500.00">
<input name="token" type="hidden" value="...">
<input name="file" type="file" />
</form>
~~~
其中,客戶端發送了自定義變量的值`x:location = Shanghai`和`x:price = 1500.00`,七牛云存儲將根據上傳資源的實際情況填寫魔法變量`$(fname)`和`$(etag)`的值。
完成上傳后,七牛云存儲便會構造出如下的回調信息:
~~~
name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm- \
RlQx_4O2&location=Shanghai&price=1500.00&uid=123
~~~
七牛云存儲將這組數據作為請求Body發送至用戶指定的回調服務器,請求方式為POST。回調服務器將接收到以下格式的請求內容:
~~~
POST /callback HTTP/1.1
Content-Type: application/x-www-form-urlencoded
User-Agent: qiniu go-sdk v6.0.0
Host: api.examples.com
Authorization: QBox iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV:tDK-3f5xF3SJYEAwsll5g=
name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm- \
RlQx_4O2&location=Shanghai&price=1500.00&uid=123
~~~
回調服務器接收到回調請求后,負責生成七牛返回給客戶端的數據(json格式),該數據作為此次回調請求的響應內容。如果回調成功,回調服務應對七牛云存儲作出類似如下的響應(注意:回調響應內容由回調服務生成,以下僅作為示例):
~~~
HTTP/1.1 200 OK
Server: nginx/1.1.19
Date: Thu, 19 Dec 2013 06:27:30 GMT
Content-Type: text/html
Transfer-Encoding: chunked
Connection: keep-alive
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Pragma: no-cache
{"success":true,"name":"sunflowerb.jpg"}
~~~
七牛云存儲將上面的回調結果返回給客戶端,客戶端接收到以下回應:
~~~
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Server: nginx/1.4.4
Date: Thu, 19 Dec 2013 08:04:56 GMT
Pragma: no-cache
X-Log: BDT:4;BDT:2;LBD:13;rs.put:1048;rs-upload.putFile:2514;UP.CB:3088;UP:5603
X-Reqid: iDYAAPBicOGXLUET
{"success":true,"name":"sunflowerb.jpg"}
~~~
如果回調失敗,七牛云存儲會將返回給應用客戶端[HTTP狀態碼579](http://developer.qiniu.com/docs/v6/api/reference/codes.html)以及詳細的失敗信息。
### 安全性
由于回調地址是公網可任意訪問的,回調服務如何確認一次回調是合法的呢?
七牛云存儲在回調時會對請求數據簽名,并將結果包含在請求頭Authorization字段中,示例如下:
~~~
Authorization:QBox iN7NgwM31j4-BZacMjPrOQBs34UG1maYCAQmhdCV:tDK-3f5xF3SJYEAwsll5g=
~~~
其中`QBox`為固定值,`iN7Ngw...dCV`為用戶的Accesskey,`tDK-3f...5g=`為簽名結果(encoded_data)
回調服務器可以通過以下方法驗證其合法性:
* 獲取AUTHORIZATION字段值中的簽名結果部分encoded_data
* 根據Accesskey選取正確的SecretKey
* 獲取明文:data = Request.URL.Path +”\n” +Request.Body
* 部分語言或框架無法直接獲取請求body的原始數據,在自行拼接時應當注意,body中的數據是經過[URL轉義](http://zh.wikipedia.org/wiki/%E7%99%BE%E5%88%86%E5%8F%B7%E7%BC%96%E7%A0%81)的
* 采用HMAC-SHA1簽名算法,對明文data簽名,秘鑰為SecretKey,比較簽名結果是否與Authorization中的encoded_data字段相同,如相同則表明這是一個合法的回調請求
以PHP語言為示例,驗證代碼如下:
~~~
/**
*C('accessKey')取得 AccessKey
*C('secretKey')取得 SecretKey
*callback.php 為回調地址的Path部分
*file_get_contents('php://input')獲取RequestBody,其值形如:
*name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm-RlQx_4O2\
*&location=Shanghai&price=1500.00&uid=123
*/
function IsQiniuCallback(){
$authstr = $_SERVER['HTTP_AUTHORIZATION'];
if(strpos($authstr,"QBox ")!=0){
return false;
}
$auth = explode(":",substr($authstr,5));
if(sizeof($auth)!=2||$auth[0]!=C('accessKey')){
return false;
}
$data = "/callback.php\n".file_get_contents('php://input');
return URLSafeBase64Encode(hash_hmac('sha1',$data,C("secretKey"), true)) == $auth[1];
}
~~~
注意:如果回調數據包含用戶的敏感數據,建議回調地址使用HTTPS協議
## 303重定向
HTTP 303重定向(參見[RFC 2616 - 10.3.4](http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.4))是HTTP 1.1規范的組成部分。
服務器可以通過返回303狀態碼告訴客戶端,本次請求的內容可以通過返回的跳轉URL取到,因此客戶端應該重定向到新的URL。
該技術被廣泛用于網頁開發領域,如在文件上傳完成后讓客戶端自動重定向到一個上傳成功的結果頁面。
七牛云存儲的資源上傳后續動作也支持303重定向功能。
在構造[上傳憑證](http://developer.qiniu.com/docs/v6/api/reference/security/upload-token.html)時,開發者可以通過設置[上傳策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)中的`returnUrl`參數以激活303重定向功能。在成功完成上傳后,服務端會向客戶端返回HTTP 303狀態碼,并在`Location`字段中攜帶上傳時指定的重定向地址。如下所示:
~~~
HTTP/1.1 303 See Other
Location: <returnUrl>
~~~
客戶端收到這樣的反饋后,應按HTTP 1.1標準將當前頁面重定向到`Location`字段所指定的URL。
主流瀏覽器都能正確的支持該跳轉操作。
如果上傳策略中還設定了自定義返回內容`returnBody`,則到時服務端返回的303響應中的`Location`字段也會包含自定義返回內容。參數值采用[URL安全的Base64編碼](http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64)。此時的響應內容如下所示:
~~~
HTTP/1.1 303 See Other
Location: <returnUrl>?upload_ret=<encoded_return_body>
~~~
如果希望返回的自定義返回內容能得到正確處理,重定向URL所對應的服務器需支持請求參數`upload_ret`。
## 自定義響應內容(returnBody)
[簡單反饋](http://developer.qiniu.com/docs/v6/api/overview/up/response/simple-response.html)只會包含資源的最基本信息,但很多情況下用戶都希望得到更多的資源信息。
用戶可以通過[資源管理](http://developer.qiniu.com/docs/v6/api/overview/rs/index.html)和[數據處理](http://developer.qiniu.com/docs/v6/api/overview/fop/index.html)功能獲得資源的擴展信息。但這些都需要用戶發起一個新請求。七牛云存儲支持在上傳請求的響應中直接返回客戶端需要的資源擴展信息。
在生成[上傳憑證](http://developer.qiniu.com/docs/v6/api/reference/security/upload-token.html)時,開發者可以通過設置[上傳策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)中的`returnBody`字段指定需要返回的信息,比如資源的大小、類型,圖片的尺寸等等。
`returnBody`實際上是一個用戶定義的反饋信息模板,**內容必須用JSON格式表達**。下面是一個示例:
~~~
{
"foo": "bar",
"name": $(fname),
"size": $(fsize),
"type": $(mimeType),
"hash": $(etag),
"w": $(imageInfo.width),
"h": $(imageInfo.height),
"color": $(exif.ColorSpace.val)
}
~~~
用戶可以在在`returnBody`中使用[變量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html),包括[魔法變量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#magicvar)和[自定義變量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar)。七牛云存儲會將這些變量替換為對應實際值,然后作為響應內容反饋給用戶,如下所示:
~~~
{
"foo": "bar",
"name": "gogopher.jpg",
"size": 214513,
"type": "image/jpg",
"hash": "Fh8xVqod2MQ1mocfI4S4KpRL6D98",
"w": 640,
"h": 480,
"color": "sRGB"
}
~~~
需要注意的是,`returnBody`不能在啟用了回調時使用。如果上傳策略中通過設置`callbackUrl`字段啟用了回調,`returnBody`將直接被忽略。
在回調模式中如果也想自定義響應內容,請在生成上傳憑證時設置上傳策略中的`callbackBody`字段。更多關于回調模式的解釋,請參見[回調(callback)](http://developer.qiniu.com/docs/v6/api/overview/up/response/callback.html)。
## 變量
變量是七牛云存儲同用戶交換數據的機制,引入變量概念的目的在于更靈活的控制上傳后續動作中的內容組織和傳遞。可以認為變量是一種占位符,七牛云存儲會將占位符按約定替換為實際內容。
在構造[上傳策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)時,可在上傳策略的`returnBody`和`callbackBody`字段內容中使用變量。
變量分為兩種:[魔法變量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#magicvar)和[自定義變量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#xvar)。魔法變量是系統提供的一系列預定義變量,可直接使用,而自定義變量則由調用方指定,通常應對應于上傳時的表單參數。服務端會將這些上傳參數的具體值返回給調用方。
### 魔法變量
魔法變量是一組預先定義的變量,可以使用?`$(var)`?或?`$(var.field_name)`?形式求值。
目前可用的魔法變量如下:
| 變量名 | 包含子項 | 變量說明 | 適用范圍 |
| :-- | :-- | --- | --- |
| bucket | 獲得上傳的目標空間名。 |
| key | 獲得文件保存在空間中的資源名。 |
| etag | 文件上傳成功后的[Etag](http://en.wikipedia.org/wiki/HTTP_ETag)。若上傳時未指定資源ID,Etag將作為資源ID使用。 |
| fname | 上傳的原始文件名。 | 不支持用于`分片上傳` |
| fsize | 資源尺寸,單位為字節。 |
| mimeType | 資源類型,比如JPG圖片的資源類型為`image/jpg`。 |
| endUser | 上傳時指定的`endUser`字段,通常用于區分不同終端用戶的請求。 |
| persistentId | 音視頻轉碼持久化的進度查詢ID。 |
| exif | 是 | 獲取所上傳圖片的[EXIF](http://en.wikipedia.org/wiki/Exchangeable_image_file_format)信息。
該變量包含子字段,比如對`$(exif.ApertureValue.val)`取值將得到該圖片拍攝時的光圈值。
| 暫不支持用于`saveKey`中 |
| imageInfo | 是 | 獲取所上傳圖片的基本信息。
該變量包含子字段,比如對`$(imageInfo.width)`取值將得到該圖片的寬度。
| 暫不支持用于`saveKey`中 |
| year | 上傳時的年份。 | 暫不支持用于’returnBody’、’callbackBody’中 |
| mon | 上傳時的月份。 | 暫不支持用于’returnBody’、’callbackBody’中 |
| day | 上傳時的日期。 | 暫不支持用于’returnBody’、’callbackBody’中 |
| hour | 上傳時的小時。 | 暫不支持用于’returnBody’、’callbackBody’中 |
| min | 上傳時的分鐘。 | 暫不支持用于’returnBody’、’callbackBody’中 |
| sec | 上傳時的秒鐘。 | 暫不支持用于’returnBody’、’callbackBody’中 |
| avinfo | 是 | 音視頻資源的元信息。 | 暫不支持用于’saveKey’中 |
| imageAve | 圖片主色調,算法由[Camera360](http://hr.camera360.com/)友情提供。 |
| ext | 上傳資源的后綴名,通過自動檢測的 mimeType 或者原文件的后綴來獲取。 | 不支持用于`分片上傳` |
| uuid | 生成uuid | 暫不支持用于’saveKey’中 |
| bodySha1 | callbackBody的sha1(hex編碼) | 只支持用于’callbackUrl’中 |
魔法變量支持`$(.)`形式的訪問子項,例如:
* $()
* $(.)
* $(..)
求值舉例:
* `$(bucket)`?- 獲得上傳目標bucket名字
* `$(imageInfo)`?- 獲取當前上傳圖片的基本屬性信息
* `$(imageInfo.height)`?- 獲取當前上傳圖片的原始高度
魔法變量不支持訪問子項為數組形式
* **不支持**$([0])
* **不支持**$(.[0])
變量`avinfo`在[`returnBody`](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html#put-policy-return-body)中返回的格式不同于url觸發返回的`avinfo`格式,`avinfo`在[魔法變量](http://developer.qiniu.com/docs/v6/api/overview/up/response/vars.html#magicvar)中的類型如下(內容經過格式化以便閱讀):
~~~
{
"audio":{
"bit_rate":"64028",
"channels":1,
"codec_name":"mp3",
"codec_type":"audio",
"duration":"30.105556",
"index":1,
"nb_frames":"1153",
"r_frame_rate":"0/0",
"sample_fmt":"s16p",
"sample_rate":"44100",
"start_time":"0.000000",
"tags":{
"creation_time":"2012-10-21 01:13:54"
}
},
"format":{
"bit_rate":"918325",
"duration":"30.106000",
"format_long_name":"QuickTime / MOV",
"format_name":"mov,mp4,m4a,3gp,3g2,mj2",
"nb_streams":2,
"size":"3455888",
"start_time":"0.000000",
"tags":{
"creation_time":"2012-10-21 01:13:54"
}
},
"video":{
"bit_rate":"856559",
"codec_name":"h264",
"codec_type":"video",
"display_aspect_ratio":"4:3",
"duration":"29.791667",
"height":480,
"index":0,
"nb_frames":"715",
"pix_fmt":"yuv420p",
"r_frame_rate":"24/1",
"sample_aspect_ratio":"1:1",
"start_time":"0.000000",
"tags":{
"creation_time":"2012-10-21 01:13:54"
},
"width":640
}
}
~~~
變量`exif`的類型如下(內容經過格式化以便閱讀,具體細節請參考[EXIF技術白皮書](http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf)):
~~~
{
"DateTime" : {
"type" : 2,
"val" : "2011:11:19 17:09:23"
},
"ExposureBiasValue" : {
"type" : 10,
"val" : "0.33 EV"
},
"ExposureTime" : {
"type" : 5,
"val" : "1/50 sec."
},
"Model" : {
"type" : 2,
"val" : "Canon EOS 600D"
},
"ISOSpeedRatings" : {
"type" : 3,
"val" : "3200"
},
"ResolutionUnit" : {
"type" : 3,
"val" : " 英寸"
},
...后續內容已省略...
}
~~~
變量`imageInfo`對應的類型如下(內容經過格式化以便閱讀):
~~~
{
"format": "jpeg",
"width": 640,
"height": 427,
"colorModel": "ycbcr"
}
~~~
如果變量取值失敗(比如在上傳策略中指定了一個并不存在的魔法變量),響應內容中對應的變量將被賦予空值。
### 自定義變量
應用客戶端則在上傳請求中設定自定義變量的值。七牛云存儲收到這些變量信息后,置換掉`returnBody`和`callbackBody`中的自定義變量設置,形成最終的反饋結果。
自定義變量的行為同魔法變量基本一致,但變量名必須以`x:`開始。下面是一個自定義變量的示例:
用戶設置了如下的`callbackBody`:
~~~
put_policy = '{
...
"callbackBody" : "name=$(fname)&hash=$(etag)&location=$(x:location)&price=$(x:price)"
...
}'
~~~
這個例子中的`$(x:location)`和`$(x:price)`就是自定義變量。
之后,用戶的客戶端構造了如下請求:
~~~
<form method="post" action="http://upload.qiniu.com/"
enctype="multipart/form-data">
<input name="key" type="hidden" value="sunflower.jpg">
<input name="x:location" type="hidden" value="Shanghai">
<input name="x:price" type="hidden" value="1500.00">
<input name="token" type="hidden" value="...">
<input name="file" type="file" />
</form>
~~~
文件上傳完成后,服務端會將請求中`x:location`和`x:price`的值,替換`callbackBody`中的自定義變量:
~~~
name=sunflower.jpg&hash=Fn6qeQi4VDLQ347NiRm- \
RlQx_4O2&location=Shanghai&price=1500.00
~~~
然后,七牛云存儲將此結果進行[URL安全的Base64編碼](http://developer.qiniu.com/docs/v6/api/overview/appendix.html#urlsafe-base64),作為回調請求的Body調用`callbackUrl`指定的回調服務器。
如果變量取值失敗(比如在上傳策略中指定了一個并不存在的表單變量),響應內容中對應的變量將被賦予空值。
## 數據預處理
在生成上傳憑證時,開發者可以通過在[上傳策略](http://developer.qiniu.com/docs/v6/api/reference/security/put-policy.html)中指定`persistentOp`和`persistentNotifyUrl`字段來設置異步數據處理動作。當資源上傳完成后,設置的數據處理動作就會被以異步方式啟動。七牛云存儲將立刻將響應內容返回給客戶端,并不會等待數據處理動作完成。
關于數據處理結果持久化相關的詳細內容,請參見[處理結果持久化(pfop)](http://developer.qiniu.com/docs/v6/api/overview/fop/persistent-fop.html)中的相關描述。