【Mark】服務(wù)端簽名直傳并設(shè)置上傳回調(diào)


公司讓我使用阿里云OSS服務(wù)器,上傳其實(shí)很快就搞定了,但是一直搞不懂我上傳上去的文件如何下載,翻閱了好多好多資料和文檔都沒法解決,直到我打了客服電話,雖然并沒有解決,但是在解決的路上看到了答案,給我氣的。阿里云服務(wù)器的文檔寫的太不友好了,對(duì)于我這樣的新手使用阿里云服務(wù)器的人來說。


背景

請(qǐng)參考Web端直傳實(shí)踐里的背景介紹。

當(dāng)采用服務(wù)端簽名后直傳方案后,問題來了,用戶上傳數(shù)據(jù)后,很多場(chǎng)景下,應(yīng)用服務(wù)器都要知道用戶上傳了哪些文件,文件名字,甚至如果是圖片的話,圖片的大小等。為此OSS開發(fā)了上傳回調(diào)功能。

用戶的請(qǐng)求邏輯

.用戶向應(yīng)用服務(wù)器取到上傳policy和回調(diào)設(shè)置。

.應(yīng)用服務(wù)器返回上傳policy和回調(diào)。

.用戶直接向OSS發(fā)送文件上傳請(qǐng)求。

.等文件數(shù)據(jù)上傳完,OSS給用戶Response前,OSS會(huì)根據(jù)用戶的回調(diào)設(shè)置,請(qǐng)求用戶的服務(wù)器。

.如果應(yīng)用服務(wù)器返回成功,那么就返回用戶成功,如果應(yīng)用服務(wù)器返回失敗,那么OSS也返回給用戶失敗。這樣確保了用戶上傳成功的照片,應(yīng)用服務(wù)器都已經(jīng)收到通知了。

.應(yīng)用服務(wù)器給OSS返回。

.OSS將應(yīng)用服務(wù)器返回的內(nèi)容返回給用戶。

簡(jiǎn)單講,就是用戶要上載一個(gè)文件到OSS服務(wù)器,而且希望上載完畢的時(shí)候自己的應(yīng)用服務(wù)能夠知道這件事,這時(shí)就需要設(shè)置一個(gè)回調(diào)函數(shù),把這件事告知用戶的應(yīng)用服務(wù)器。這樣當(dāng)OSS收到用戶的上傳請(qǐng)求之后,開始上傳,傳完之后不會(huì)直接給用戶返回結(jié)果,而是先通知用戶的應(yīng)用服務(wù)器:“我上傳完畢了”,然后應(yīng)用服務(wù)器告訴OSS:“我知道啦,你幫我轉(zhuǎn)達(dá)給我的主人吧”,于是OSS就把結(jié)果轉(zhuǎn)達(dá)給用戶了。

快速使用

只要以下三步,就能實(shí)現(xiàn)文件快速通過網(wǎng)頁上傳到OSS,并且OSS會(huì)回調(diào)通知到用戶設(shè)置的應(yīng)用服務(wù)器。

.設(shè)置成自己的id、key、bucket。

設(shè)置方法:修改php/get.php,將變量$id設(shè)成AccessKeyId,$key設(shè)置成AccessKeySecret,$host設(shè)置成bucket+endpoint。

.$id='xxxxxx';

.$key='xxxxx';

.$host='http://post-test.oss-cn-hangzhou.aliyuncs.com

.

.為了瀏覽安全,必須為bucket設(shè)置Cors,參照下文。

.設(shè)置自己的回調(diào)URL,如http://abc.com/test.html(必須公網(wǎng)訪問得通),即自己的回調(diào)服務(wù)器地址,OSS會(huì)在文件上傳完成后,把文件上傳信息,通過自己設(shè)置的回調(diào)URL(http://abc.com/test.html)發(fā)送給應(yīng)用服務(wù)器。

設(shè)置方法:修改php/get.php,(這個(gè)回調(diào)服務(wù)端代碼實(shí)例參考下文)

.$callbackUrl="http://abc.com/test.html";

下面講解一下核心邏輯。

核心代碼解析

代碼要添加的內(nèi)容如下:

.new_multipart_params={

.'key':key+'${filename}',

.'policy':policyBase64,

.'OSSAccessKeyId':accessid,

.'success_action_status':'200',//讓服務(wù)端返回200,不然,默認(rèn)會(huì)返回204

.'callback':callbackbody,

.'signature':signature,

.};

上述的callbackbody是php服務(wù)端返回的。在本例中,從后端php取到的內(nèi)容如下:

.{"accessid":"6MKOqxGiGU4AUk44",

."host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",

."policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDo1MjoyOVoiLCJjdb25kaXRpb25zIjpbWyJjdb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",

."signature":"VsxOcOudxDbtNSvz93CLaXPz+4s=",

."expire":1446727949,

."callback":"eyJjYWxsYmFja1VybCI6Imh0dHA6Ly9vc3MtZGVtby5hbGl5dW5jcy5jdb206MjM0NTAiLCJjYWxsYmFja0hvc3QiOiJvc3MtZGVtby5hbGl5dW5jcy5jdb20iLCJjYWxsYmFja0JvZHkiOiJmaWxlbmFtZT0ke29iamVjdH0mc2l6ZT0ke3NpemV9Jm1pbWVUeXBlPSR7bWltZVR5cGV9JmhlaWdodD0ke2ltYWdlSW5mby5oZWlnaHR9JndpZHRoPSR7aW1hZ2VJdbmZvLndpZHRofSIsImNhbGxiYWNrQm9keVR5cGUiOiJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWQifQ==","dir":"user-dirs/"}

上面提到callbackbody,就是上述返回結(jié)果里面的callback內(nèi)容,經(jīng)過base64編碼后生成的。

解碼后的內(nèi)容如下:

.{"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"}

內(nèi)容的解析如下:

CallbackUrl: OSS往這個(gè)機(jī)器發(fā)送的url請(qǐng)求。

callbackHost: OSS發(fā)送這個(gè)請(qǐng)求時(shí),請(qǐng)求頭部所帶的Host頭。

callbackBody: OSS請(qǐng)求時(shí),發(fā)送給應(yīng)用服務(wù)器的內(nèi)容,可以包括文件的名字、大小、類型,如果是圖片可以是圖片的高度、寬度。

callbackBodyType:請(qǐng)求發(fā)送的Content-Type。

回調(diào)應(yīng)用服務(wù)器

在用戶的請(qǐng)求邏輯中,很重要的地方就是第4步和第5步,OSS與應(yīng)用服務(wù)器交互的時(shí)候,用戶可能會(huì)有以下疑問:

問題1:如果我是開發(fā)者,那么我要怎么樣確認(rèn)請(qǐng)求是從OSS發(fā)送過來的呢?

答案:OSS發(fā)送請(qǐng)求時(shí),會(huì)跟應(yīng)用服務(wù)器構(gòu)造簽名。兩者通過簽名保證。

問題2:這個(gè)簽名是怎么做的?或者有示例代碼嗎?

答案:有的。上面的例子里面是Callback應(yīng)用服務(wù)器的例子:http://oss-demo.aliyuncs.com:23450(目前只支持Linux)。

運(yùn)行方案:在Linux下面直接執(zhí)行里面的文件python callback_app_server.py即可,程序自實(shí)現(xiàn)了一個(gè)簡(jiǎn)單的http server,運(yùn)行該程序可能需要安裝rsa的依賴。

問題3:為何我的應(yīng)用服務(wù)器收到的回調(diào)請(qǐng)求沒有Authotization頭?

答案:有些Web server會(huì)將Authorization頭自行解析掉,比如apache2,因此需要設(shè)置成不解析這個(gè)頭部。以apache2為例,具體設(shè)置方法為:

i.打開rewrite模塊,執(zhí)行命令:a2enmod rewrite;

ii.修改配置文件/etc/apache2/apache2.conf(apache2的安裝路徑不同會(huì)有不一樣)。將Allow Override設(shè)置成All,然后添加下面兩條配置:

RewriteEngine on

RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]

示例程序只是完成了如何檢查應(yīng)用服務(wù)器收到的簽名,用戶要自行增加對(duì)應(yīng)用服務(wù)器收到回調(diào)的內(nèi)容的格式解析。


JS的源代碼

親測(cè)實(shí)用有效

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();

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理,服務(wù)發(fā)現(xiàn),斷路器,智...
    卡卡羅2017閱讀 136,502評(píng)論 19 139
  • 單例模式 適用場(chǎng)景:可能會(huì)在場(chǎng)景中使用到對(duì)象,但只有一個(gè)實(shí)例,加載時(shí)并不主動(dòng)創(chuàng)建,需要時(shí)才創(chuàng)建 最常見的單例模式,...
    Obeing閱讀 2,311評(píng)論 1 10
  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語法,類相關(guān)的語法,內(nèi)部類的語法,繼承相關(guān)的語法,異常的語法,線程的語...
    子非魚_t_閱讀 34,623評(píng)論 18 399
  • 工廠模式類似于現(xiàn)實(shí)生活中的工廠可以產(chǎn)生大量相似的商品,去做同樣的事情,實(shí)現(xiàn)同樣的效果;這時(shí)候需要使用工廠模式。簡(jiǎn)單...
    舟漁行舟閱讀 8,110評(píng)論 2 17
  • 《ijs》速成開發(fā)手冊(cè)3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 10...
    葉染柒丶閱讀 5,617評(píng)論 0 7

友情鏈接更多精彩內(nèi)容