來源:Web 端直傳實踐簡介
目的
通過三個例子介紹如何在 HTML 表單提交直傳 OSS:
- 簽名在客戶端(javascript)完成,然后直接通過表單上傳到 OSS。
- 簽名在服務端(php)完成,然后直接通過表單上傳到 OSS。
- 簽名在服務端(php)完成,并且服務端設置了上傳后回調。然后直接通過表單上傳到 OSS,OSS回調完應用服務器再返回給用戶。
基礎篇:JavaScript 客戶端簽名直傳

OSS 的 PostObject API 細節(jié)可以參照這里。
關鍵代碼
只要將 plupload 發(fā)送 POST 請求時,帶上 OSS 簽名即可。
var uploader = new plupload.Uploader({
runtimes: 'html5',
browse_button: 'selectfiles',
container: document.getElementById('container'),
url: host,
multipart_params: {
'Filename': '${filename}',
'key': '${filename}',
'policy': policyBase64,
'OSSAccessKeyId': accessid,
'success_action_status': '200',
'signature': signature
},
})
在這里有一點請注意一下,就是 'Filename': '${filename}', 這一段代碼的作用是表示上傳后保持原來的文件文字。如果您想上傳到特定目錄如abc下,文件名保持成原來的文件名,那么應該這樣寫:
// ...
'Filename': 'abc/' + '${filename}',
// ...
有時候要把用戶上傳的文件,設置成隨機文件名,后綴保持跟客戶端文件一致。如果想在上傳時就固定設置成隨機文件名,可以將函數(shù)改成如下:
function check_object_radio(){
g_object_name_type = 'random_name';
}
如果想固定設置成用戶的文件,可以將函數(shù)改成:
function check_object_radio() {
g_object_name_type = 'local_name';
}
進階篇:應用服務器 php 返回簽名
采用 JS 客戶端直接簽名有一個很嚴重的安全隱患。就是 OSS AccessId/AccessKey 暴露在前端頁面。可以隨意拿到 AccessId/AccessKey,這是非常不安全的做法。 本小節(jié)將此例子進化,簽名及上傳 policy 從后端 php 代碼取。
請求邏輯是:
- 客戶端要上傳圖片時,到應用服務器取上傳的 policy 及簽名。
- 客戶端拿到簽名直接上傳到 OSS。

獲取上傳后的文件名
如果要知道文件上傳成功后的文件名,pupload 會調用 FileUploaded 事件。 如下:
FileUploaded: function(up, file, info){
var item = document.getElementById(file.id).getElementsByTagName('b')[0];
if (info.status == 200) {
item.innerHTML = 'upload to oss success, object name: ' + get_uploaded_object_name(file.name);
} else {
item.innerHTML = info.response;
}
}
上傳簽名
javaScript 最主要是從后端取到 policyBase64、accessid、signature 這三個變量。 往后端取這三個變量核心代碼如下:
phpUrl = './php/get.php';
xmlhttp.open( "GET", phpUrl, false );
xmlhttp.send( null );
var obj = eval ("(" + xmlhttp.responseText+ ")");
host = obj['host']
policyBase64 = obj['policy']
accessid = obj['accessid']
signature = obj['signature']
expire = parseInt(obj['expire'])
key = obj['dir']
現(xiàn)在解析一下 xmlhttp.responseText(以下僅為示例,并不一定要求是以下的格式,但是必須有signature、accessid、policy這三個值)。
{
"accessid":"6MKOqxGiGU4AUk44",
"host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
"policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
"signature":"I2u57FWjTKqX/AE6doIdyff151E=",
"expire":1446726203,
"dir":"user-dir/"
}
- accessid: 指的用戶請求的accessid。注意僅知道accessid, 對數(shù)據不會有影響。
- host: 指的是用戶要往哪個域名發(fā)往上傳請求。
- policy:指的是用戶表單上傳的策略policy,是經過base64編碼過的字符串。
- signature:是對上述第三個變量policy簽名后的字符串。
- expire:指的是當前上傳策略失效時間,這個變量并不會發(fā)送到OSS,因為這個已經指定在policy里面。
現(xiàn)在分析一下policy的內容,將其解碼后的內容是:
{
"expiration":"2015-11-05T20:23:23Z",
"conditions":[
["content-length-range",0,1048576000],
["starts-with","$key","user-dir/"]
]
}
這里有一個關鍵的地方,PolicyText 指定了該 Policy 上傳失效的最終時間。即在這個失效時間之前,都可以利用這個 policy 上傳文件,所以沒有必要每次上傳,都去后端取簽名。為了減少后端的壓力,這里的設計思路是:初始化上傳時,每上傳一個文件后,取一次簽名。然后再上傳時,將當前時間跟簽名時間對比,看簽名時間是否失效了。如果失效了,就重新取一次簽名,如果沒有失效就不取。這里就用到了變量 expire。核心代碼如下:
now = timestamp = Date.parse(new Date()) / 1000;
//可以判斷當前expire是否超過了當前時間,如果超過了當前時間,就重新取一下,3s 做為緩沖[/color]
if (expire < now + 3){
// ...
phpUrl = './php/get.php'
xmlhttp.open( "GET", phpUrl, false );
xmlhttp.send( null );
// ...
}
終極篇:應用服務器 php 返回簽名及采用上傳回調
當采用服務端簽名后直傳方案后,問題來了,用戶上傳數(shù)據后,很多場景下,應用服務器都要知道用戶上傳了哪些文件,文件名字,甚至如果是圖片的話,圖片的大小等。為此OSS開發(fā)了上傳回調功能。
用戶的請求邏輯
- 用戶向應用服務器取到上傳 policy 和回調設置。
- 應用服務器返回上傳 policy 和回調。
- 用戶直接向 OSS 發(fā)送文件上傳請求。
- 等文件數(shù)據上傳完,OSS 給用戶 Response 前,OSS 會根據用戶的回調設置,請求用戶的服務器。
- 如果應用服務器返回成功,那么就返回用戶成功,如果應用服務器返回失敗,那么 OSS 也返回給用戶失敗。這樣確保了用戶上傳成功的照片,應用服務器都已經收到通知了。
- 應用服務器給 OSS 返回。
- OSS 將應用服務器返回的內容返回給用戶。
