公司讓我使用阿里云OSS服務器,上傳其實很快就搞定了,但是一直搞不懂我上傳上去的文件如何下載蝗蛙,翻閱了好多好多資料和文檔都沒法解決痕寓,直到我打了客服電話记餐,雖然并沒有解決牌捷,但是在解決的路上看到了答案好啰,給我氣的洲守。阿里云服務器的文檔寫的太不友好了疑务,對于我這樣的新手使用阿里云服務器的人來說。
背景
請參考Web端直傳實踐里的背景介紹梗醇。
當采用服務端簽名后直傳方案后知允,問題來了,用戶上傳數據后叙谨,很多場景下温鸽,應用服務器都要知道用戶上傳了哪些文件,文件名字,甚至如果是圖片的話涤垫,圖片的大小等姑尺。為此OSS開發(fā)了上傳回調功能。
用戶的請求邏輯
.用戶向應用服務器取到上傳policy和回調設置蝠猬。
.應用服務器返回上傳policy和回調切蟋。
.用戶直接向OSS發(fā)送文件上傳請求。
.等文件數據上傳完榆芦,OSS給用戶Response前柄粹,OSS會根據用戶的回調設置,請求用戶的服務器歧杏。
.如果應用服務器返回成功镰惦,那么就返回用戶成功,如果應用服務器返回失敗犬绒,那么OSS也返回給用戶失敗旺入。這樣確保了用戶上傳成功的照片,應用服務器都已經收到通知了凯力。
.應用服務器給OSS返回茵瘾。
.OSS將應用服務器返回的內容返回給用戶。
簡單講咐鹤,就是用戶要上載一個文件到OSS服務器拗秘,而且希望上載完畢的時候自己的應用服務能夠知道這件事,這時就需要設置一個回調函數祈惶,把這件事告知用戶的應用服務器雕旨。這樣當OSS收到用戶的上傳請求之后,開始上傳捧请,傳完之后不會直接給用戶返回結果凡涩,而是先通知用戶的應用服務器:“我上傳完畢了”,然后應用服務器告訴OSS:“我知道啦疹蛉,你幫我轉達給我的主人吧”活箕,于是OSS就把結果轉達給用戶了。
快速使用
只要以下三步可款,就能實現文件快速通過網頁上傳到OSS育韩,并且OSS會回調通知到用戶設置的應用服務器。
.設置成自己的id闺鲸、key筋讨、bucket。
設置方法:修改php/get.php摸恍,將變量$id設成AccessKeyId版仔,$key設置成AccessKeySecret,$host設置成bucket+endpoint。
.$id='xxxxxx';
.$key='xxxxx';
.$host='http://post-test.oss-cn-hangzhou.aliyuncs.com
.
.為了瀏覽安全蛮粮,必須為bucket設置Cors,參照下文。
.設置自己的回調URL谜慌,如http://abc.com/test.html(必須公網訪問得通),即自己的回調服務器地址然想,OSS會在文件上傳完成后,把文件上傳信息欣范,通過自己設置的回調URL(http://abc.com/test.html)發(fā)送給應用服務器变泄。
設置方法:修改php/get.php,(這個回調服務端代碼實例參考下文)
.$callbackUrl="http://abc.com/test.html";
下面講解一下核心邏輯恼琼。
核心代碼解析
代碼要添加的內容如下:
.new_multipart_params={
.'key':key+'${filename}',
.'policy':policyBase64,
.'OSSAccessKeyId':accessid,
.'success_action_status':'200',//讓服務端返回200,不然妨蛹,默認會返回204
.'callback':callbackbody,
.'signature':signature,
.};
上述的callbackbody是php服務端返回的。在本例中晴竞,從后端php取到的內容如下:
.{"accessid":"6MKOqxGiGU4AUk44",
."host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
."policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDo1MjoyOVoiLCJjdb25kaXRpb25zIjpbWyJjdb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
."signature":"VsxOcOudxDbtNSvz93CLaXPz+4s=",
."expire":1446727949,
."callback":"eyJjYWxsYmFja1VybCI6Imh0dHA6Ly9vc3MtZGVtby5hbGl5dW5jcy5jdb206MjM0NTAiLCJjYWxsYmFja0hvc3QiOiJvc3MtZGVtby5hbGl5dW5jcy5jdb20iLCJjYWxsYmFja0JvZHkiOiJmaWxlbmFtZT0ke29iamVjdH0mc2l6ZT0ke3NpemV9Jm1pbWVUeXBlPSR7bWltZVR5cGV9JmhlaWdodD0ke2ltYWdlSW5mby5oZWlnaHR9JndpZHRoPSR7aW1hZ2VJdbmZvLndpZHRofSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==","dir":"user-dirs/"}
上面提到callbackbody蛙卤,就是上述返回結果里面的callback內容,經過base64編碼后生成的噩死。
解碼后的內容如下:
.{"callbackUrl":"http://oss-demo.aliyuncs.com:23450",
."callbackHost":"oss-demo.aliyuncs.com",
."callbackBody":"filename=${object}&size=${size}&mimeType=${mimeType}&height=${imageInfo.height}&width=${imageInfo.width}",
."callbackBodyType":"application/x-www-form-urlencoded"}
內容的解析如下:
CallbackUrl: OSS往這個機器發(fā)送的url請求颤难。
callbackHost: OSS發(fā)送這個請求時,請求頭部所帶的Host頭已维。
callbackBody: OSS請求時行嗤,發(fā)送給應用服務器的內容,可以包括文件的名字垛耳、大小栅屏、類型,如果是圖片可以是圖片的高度堂鲜、寬度栈雳。
callbackBodyType:請求發(fā)送的Content-Type。
回調應用服務器
在用戶的請求邏輯中泡嘴,很重要的地方就是第4步和第5步甫恩,OSS與應用服務器交互的時候,用戶可能會有以下疑問:
問題1:如果我是開發(fā)者酌予,那么我要怎么樣確認請求是從OSS發(fā)送過來的呢磺箕?
答案:OSS發(fā)送請求時,會跟應用服務器構造簽名抛虫。兩者通過簽名保證松靡。
問題2:這個簽名是怎么做的?或者有示例代碼嗎建椰?
答案:有的雕欺。上面的例子里面是Callback應用服務器的例子:http://oss-demo.aliyuncs.com:23450(目前只支持Linux)。
運行方案:在Linux下面直接執(zhí)行里面的文件python callback_app_server.py即可,程序自實現了一個簡單的http server屠列,運行該程序可能需要安裝rsa的依賴啦逆。
問題3:為何我的應用服務器收到的回調請求沒有Authotization頭?
答案:有些Web server會將Authorization頭自行解析掉笛洛,比如apache2夏志,因此需要設置成不解析這個頭部。以apache2為例苛让,具體設置方法為:
i.打開rewrite模塊沟蔑,執(zhí)行命令:a2enmod rewrite;
ii.修改配置文件/etc/apache2/apache2.conf(apache2的安裝路徑不同會有不一樣)狱杰。將Allow Override設置成All瘦材,然后添加下面兩條配置:
RewriteEngine on
RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]
示例程序只是完成了如何檢查應用服務器收到的簽名,用戶要自行增加對應用服務器收到回調的內容的格式解析仿畸。
JS的源代碼
親測實用有效
accessid = ''
accesskey = ''
host = ''
policyBase64 = ''
signature = ''
callbackbody = ''
filename = ''
key = ''
expire = 0
g_object_name = ''
g_object_name_type = ''
now = timestamp = Date.parse(new Date()) / 1000;
function send_request()
{
var xmlhttp = null;
if (window.XMLHttpRequest)
{
xmlhttp=new XMLHttpRequest();
}
else if (window.ActiveXObject)
{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
if (xmlhttp!=null)
{
serverUrl = './php/get.php'
xmlhttp.open( "GET", serverUrl, false );
xmlhttp.send( null );
return xmlhttp.responseText
}
else
{
alert("Your browser does not support XMLHTTP.");
}
};
function check_object_radio() {
var tt = document.getElementsByName('myradio');
for (var i = 0; i < tt.length ; i++ )
{
if(tt[i].checked)
{
g_object_name_type = tt[i].value;
break;
}
}
}
function get_signature()
{
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;
};
function random_string(len) {
len = len || 32;
var chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678';
var maxPos = chars.length;
var pwd = '';
for (i = 0; i < len; i++) {
pwd += chars.charAt(Math.floor(Math.random() * maxPos));
}
return pwd;
}
function get_suffix(filename) {
pos = filename.lastIndexOf('.')
suffix = ''
if (pos != -1) {
suffix = filename.substring(pos)
}
return suffix;
}
function calculate_object_name(filename)
{
if (g_object_name_type == 'local_name')
{
g_object_name += "${filename}"
}
else if (g_object_name_type == 'random_name')
{
suffix = get_suffix(filename)
g_object_name = key + random_string(10) + suffix
}
return ''
}
function get_uploaded_object_name(filename)
{
if (g_object_name_type == 'local_name')
{
tmp_name = g_object_name
tmp_name = tmp_name.replace("${filename}", filename);
return tmp_name
}
else if(g_object_name_type == 'random_name')
{
return g_object_name
}
}
function set_upload_param(up, filename, ret)
{
if (ret == false)
{
ret = get_signature()
}
g_object_name = key;
if (filename != '') { suffix = get_suffix(filename)
calculate_object_name(filename)
}
new_multipart_params = {
'key' : g_object_name,
'policy': policyBase64,
'OSSAccessKeyId': accessid,
'success_action_status' : '200',
'callback' : callbackbody,
'signature': signature,
};
up.setOption({
'url': host,
'multipart_params': new_multipart_params
});
up.start();
}
var uploader = new plupload.Uploader({
runtimes : 'html5,flash,silverlight,html4',
browse_button : 'selectfiles',
//multi_selection: false,
container: document.getElementById('container'),
flash_swf_url : 'lib/plupload-2.1.2/js/Moxie.swf',
silverlight_xap_url : 'lib/plupload-2.1.2/js/Moxie.xap',
url : 'http://oss.aliyuncs.com',
filters: {
mime_types : [?
{ title : "Image files", extensions : "jpg,gif,png,bmp" },
{ title : "Zip files", extensions : "zip,rar" }
],
max_file_size : '10mb',
prevent_duplicates : true?
},
init: {
PostInit: function() {
document.getElementById('ossfile').innerHTML = '';
document.getElementById('postfiles').onclick = function() {
set_upload_param(uploader, '', false);
return false;
};
},
FilesAdded: function(up, files) {
plupload.each(files, function(file) {
document.getElementById('ossfile').innerHTML += '
' + file.name + ' (' + plupload.formatSize(file.size) + ')'+'
'+'';
});
},
BeforeUpload: function(up, file) {
check_object_radio();
set_upload_param(up, file.name, true);
},
UploadProgress: function(up, file) {
var d = document.getElementById(file.id);
d.getElementsByTagName('b')[0].innerHTML = '' + file.percent + "%";
var prog = d.getElementsByTagName('div')[0];
var progBar = prog.getElementsByTagName('div')[0]
progBar.style.width= 2*file.percent+'px';
progBar.setAttribute('aria-valuenow', file.percent);
},
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) + ':' + info.response;
}
else if (info.status == 203)
{
document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = ':' + info.response;
}
else
{
document.getElementById(file.id).getElementsByTagName('b')[0].innerHTML = info.response;
}
},
Error: function(up, err) {
if (err.code == -600) {
document.getElementById('console').appendChild(document.createTextNode("\n"));
}
else if (err.code == -601) {
document.getElementById('console').appendChild(document.createTextNode("\n"));
}
else if (err.code == -602) {
document.getElementById('console').appendChild(document.createTextNode("\n"));
}
else
{
document.getElementById('console').appendChild(document.createTextNode("\nError xml:" + err.response));
}
}
}
});
uploader.init();