本文主要介紹如何基于Post Policy的使用規則在服務端通過各種語言代碼完成簽名,并且設置上傳回調,然后通過表單直傳數據到OSS。
背景信息
大多數情況下,用戶上傳文件后,應用服務器需要知道用戶上傳了哪些文件以及文件名;如果上傳了圖片,還需要知道圖片的大小等,為此OSS提供了上傳回調方案。
流程介紹
當用戶要上傳一個文件到OSS,而且希望將上傳的結果返回給應用服務器時,需要設置一個回調函數,將請求告知應用服務器。用戶上傳完文件后,不會直接得到返回結果,而是先通知應用服務器,再把結果轉達給用戶。
操作示例
流程解析
以下根據流程講解核心代碼和消息內容。
用戶向應用服務器請求上傳Policy和回調。
在客戶端源碼中的
upload.js
文件中,如下代碼片段的變量serverUrl
的值可以用來設置應用服務器的URL。設置好之后,客戶端會向該serverUrl
發送Get請求來獲取需要的信息。// serverUrl是用戶獲取簽名和Policy等信息的應用服務器的URL,請將下面的IP和Port配置為您自己的真實信息。 serverUrl = 'http:/88.88.XX.XXX:8888';
應用服務器返回上傳Policy和回調設置代碼。
應用服務器側的簽名直傳服務會處理客戶端發過來的Get請求消息,您可以設置對應的代碼讓應用服務器能夠給客戶端返回正確的消息。各個語言版本的配置文檔中都有明確的說明供您參考。
以下是簽名直傳服務返回給客戶端消息Body內容的示例,Body的內容將作為客戶端上傳文件的重要參數。
{ "accessid":"LTAI**********", "host":"http://post-test.oss-cn-hangzhou.aliyuncs.com", "policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8i****", "signature":"I2u5**********", "expire":1446727949, "callback":"eyJjYWxsYmFja1VybCI6Imh0dHA6Ly9vc3MtZGVtby5hbGl5dW5jcy5jb206MjM0NTAiLAoiY2FsbGJhY2tCb2R5IjoiZmlsZW5hbWU9JHtvYmplY3R9JnNpemU9JHtzaXplfSZtaW1lVHlwZT0ke21pbWVUeXBlfSZoZWlnaHQ9JHtpbWFnZUluZm8uaGVpZ2h0fSZ3aWR0aD0ke2ltYWdlSW5mby53aWR0aH0iLAoiY2FsbGJhY2tCb2R5VHlwZSI6ImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZCJ9", "dir":"user-dirs/" }
上述示例的callback內容采用的是Base64編碼。經過Base64解碼后的內容如下:
{"callbackUrl":"http://oss-demo.aliyuncs.com:23450", "callbackBody":"filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}", "callbackBodyType":"application/x-www-form-urlencoded"}
說明以上僅為回調示例,您可以通過修改服務端代碼自行設置回調。
參數
說明
callbackUrl
OSS向服務器發送的URL請求。
callbackHost
OSS發送該請求時,請求頭部所帶的Host頭。
callbackBody
OSS發送給應用服務器的內容。如果是文件,可以是文件的名稱、大小、類型等。如果是圖片,可以是圖片的高度、寬度等。
callbackBodyType
請求發送的Content-Type。
取值:
application/x-www-form-urlencoded(默認值)
application/json
用戶直接向OSS發送文件上傳請求。
在客戶端源碼
upload.js
文件中,callbackbody
的值是步驟2中應用服務器返回給客戶端消息Body中Callback的內容。new_multipart_params = { 'key' : key + '${filename}', 'policy': policyBase64, 'OSSAccessKeyId': accessid, // 設置服務端返回狀態碼為200,不設置則默認返回狀態碼204。 'success_action_status' : '200', 'callback': callbackbody, 'signature': signature, };
OSS根據用戶的回調設置,發送回調請求給應用服務器。
客戶端上傳文件到OSS結束后,OSS解析客戶端的上傳回調設置,發送Post回調請求給應用服務器。消息內容示例如下:
說明此示例使用的是V1版本簽名,如需使用V4簽名發送請求,可參考V1簽名升級為V4簽名指引。
Hypertext Transfer Protocol POST / HTTP/1.1\r\n Host47.97.XXX.XX53\r\n Connection: close\r\n Content-Length: 76\r\n Authorization: fsNx********//a8x6v2lI1********\r\n Content-MD5: eiEMyp7lbL8KStPBzMdr9w==\r\n Content-Type: application/x-www-form-urlencoded\r\n Date: Sat, 15 Sep 2018 10:24:12 GMT\r\n User-Agent: aliyun-oss-callback\r\n x-oss-additional-headers: \r\n x-oss-bucket: signedcallback\r\n x-oss-owner: 137918634953****\r\n x-oss-pub-key-url: aHR0cDovL2dvc3NwdWJsaWMuYWxpY2RuLmNvbS9jYWxsYmFja19wdWJfa2V5X3YxLnaH****\r\n x-oss-request-id: 534B371674E88A4D8906****\r\n x-oss-requester: 137918634953****\r\n x-oss-signature-version: 1.0\r\n x-oss-tag: CALLBACK\r\n eagleeye-rpcid: 0.1\r\n \r\n [Full request URI: http://47.xx.xx.53/] [HTTP request 1/1] [Response in frame: 39] File Data: 76 bytes HTML Form URL Encoded: application/x-www-form-urlencoded Form item: "filename" = ".snappython.png" Form item: "size" = "6014" Form item: "mimeType" = "image/png" Form item: "height" = "221"
應用服務器返回響應給OSS。
應用服務器根據OSS發送消息中的
authorization
來進行驗證,如果驗證通過,則向OSS返回如下JSON格式的成功消息。{ "String value": "ok", "Key": "Status" }
OSS將應用服務器返回的消息返回給用戶。
客戶端源碼解析
客戶端源碼下載地址:aliyun-oss-appserver-js-master.zip
客戶端JavaScript代碼使用的是Plupload組件。Plupload是一款簡單易用且功能強大的文件上傳工具, 支持多種上傳方式,包括HTML5、Flash、SilverLight、HTML4。它會智能檢測當前環境,選擇最適合的上傳方式,并且會優先采用HTML5方式。詳情請參見Plupload官網。
以下為常見功能的代碼示例:
設置成隨機文件名
若上傳時采用固定格式的隨機文件名,且后綴跟客戶端文件名保持一致,可以將函數改為:
function check_object_radio() { g_object_name_type = 'random_name'; }
設置成用戶的文件名
如果想在上傳時設置成用戶的文件名,可以將函數改為:
function check_object_radio() { g_object_name_type = 'local_name'; }
設置上傳目錄
上傳的目錄由服務端指定, 每個客戶端只能上傳到指定的目錄,實現安全隔離。下面的代碼以PHP為例,將上傳目錄改成abc/,注意目錄必須以正斜線(/)結尾。
$dir ='abc/';
設置上傳過濾條件
您可以利用Plupload的屬性filters設置上傳的過濾條件,如設置只能上傳圖片、上傳文件的大小、不能有重復上傳等。
var uploader = new plupload.Uploader({ …… filters: { mime_types : [ // 只允許上傳圖片和ZIP文件。 { title : "Image files", extensions : "jpg,gif,png,bmp" }, { title : "Zip files", extensions : "zip" } ], // 最大只能上傳400KB的文件。 max_file_size : '400kb', // 不允許選取重復文件。 prevent_duplicates : true },
mime_types:限制上傳的文件后綴。
max_file_size:限制上傳的文件大小。
prevent_duplicates:限制不能重復上傳。
獲取上傳后的文件名
如果要知道文件上傳成功后的文件名,可以用Plupload調用FileUploaded事件獲取,如下所示:
FileUploaded: function(up, file, info) { if (info.status == 200) { document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = 'upload to oss success, object name:' + get_uploaded_object_name(file.name); } else { document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = info.response; } }
可以利用
get_uploaded_object_name(file.name)
函數,得到上傳到OSS的文件名,其中file.name
記錄了本地文件上傳的名稱。上傳簽名
JavaScript可以從服務端獲取policyBase64、accessid、signature這三個變量,核心代碼如下:
function get_signature() { // 判斷expire的值是否超過了當前時間,如果超過了當前時間,則重新獲取簽名,緩沖時間為3秒。 now = timestamp = Date.parse(new Date()) / 1000; if (expire < now + 3) { body = send_request() var obj = eval ("(" + body + ")"); host = obj['host'] policyBase64 = obj['policy'] accessid = obj['accessid'] signature = obj['signature'] expire = parseInt(obj['expire']) callbackbody = obj['callback'] key = obj['dir'] return true; } return false; };
從服務端返回的消息解析如下:
說明以下僅為示例,并不要求相同的格式,但必須包含accessid、policy、signature三個值。
{"accessid":"LTAI**********", "host":"http://post-test.oss-cn-hangzhou.aliyuncs.com", "policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8i****", "signature":"I2u5**********", "expire":1446726203,"dir":"user-dir/"}
accessid:用戶請求的accessid。
host:用戶要往哪個域名發送上傳請求。
policy:用戶表單上傳的策略(Policy),是經過Base64編碼過的字符串。詳情請參見Post Policy。
signature:對Policy簽名后的字符串。
expire:上傳策略Policy失效時間,在服務端指定。失效時間之前都可以利用此Policy上傳文件,無需每次上傳都去服務端獲取簽名。
說明為了減少服務端的壓力,初始化上傳時,每上傳一個文件,獲取一次簽名。再次上傳文件時,對當前時間與簽名時間進行比較,并查看簽名時間是否失效。如果簽名已失效,則重新獲取一次簽名;如果簽名未失效,則使用之前的簽名。
解析Policy的內容如下:
{"expiration":"2015-11-05T20:23:23Z", "conditions":[["content-length-range",0,1048576000],// 上傳文件的大小限制默認為5 GB。您可以自定義上傳文件的大小限制。如果超過此限制,文件上傳到OSS會報錯。 ["starts-with","$key","user-dir/"]]}
上面Policy中增加了starts-with,用來指定此次上傳的文件名必須以user-dir開頭,用戶也可以自行指定。增加starts-with的原因是,在眾多場景下,一個應用對應一個Bucket。為了防止數據覆蓋,用戶上傳到OSS的每個文件都可以有特定的前綴。但這樣存在一個問題,用戶獲取到這個Policy后,在失效期內都能修改上傳前綴,從而上傳到其他用戶的目錄下。為解決該問題,可在應用服務器端指定用戶上傳文件的前綴。這樣即便用戶獲取了Policy也沒有辦法上傳到其他用戶的目錄,從而保證了數據的安全性。
設置應用服務器的地址
在客戶端源碼
upload.js
文件中,如下代碼片段的變量serverUrl
的值可以用來設置應用服務器的URL,設置完成后,客戶端會向該serverUrl
發送Get請求來獲取信息。// serverUrl是用戶獲取簽名和Policy等信息的應用服務器的URL,請將下面的IP和Port配置為您自己的真實信息。 serverUrl = 'http:/88.88.XX.XX8:8888'
常見問題
前端如何實現批量上傳文件?
OSS沒有開放批量上傳接口,如果需要批量上傳,您可以使用一個循環去上傳所有文件,示例與上傳單個文件一致。
使用服務端簽名直傳并設置上傳回調時支持自定義回調請求的header頭部信息么?
不支持。您只能自定義回調參數,也就是回調的body,但是OSS回調請求您的callback url時攜帶的header無法自定義。