【Mark】服務端簽名直傳并設置上傳回調


公司讓我使用阿里云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();

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末食棕,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子颁湖,更是在濱河造成了極大的恐慌宣蠕,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件甥捺,死亡現場離奇詭異抢蚀,居然都是意外死亡,警方通過查閱死者的電腦和手機镰禾,發(fā)現死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門皿曲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人吴侦,你說我怎么就攤上這事屋休。” “怎么了备韧?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵劫樟,是天一觀的道長。 經常有香客問我织堂,道長叠艳,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任易阳,我火速辦了婚禮附较,結果婚禮上,老公的妹妹穿的比我還像新娘潦俺。我一直安慰自己拒课,他們只是感情好徐勃,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著早像,像睡著了一般僻肖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上卢鹦,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天檐涝,我揣著相機與錄音,去河邊找鬼法挨。 笑死,一個胖子當著我的面吹牛幅聘,可吹牛的內容都是我干的凡纳。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼帝蒿,長吁一口氣:“原來是場噩夢啊……” “哼荐糜!你這毒婦竟也來了?” 一聲冷哼從身側響起葛超,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤暴氏,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后绣张,有當地人在樹林里發(fā)現了一具尸體答渔,經...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年侥涵,在試婚紗的時候發(fā)現自己被綠了沼撕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡芜飘,死狀恐怖务豺,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情嗦明,我是刑警寧澤笼沥,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站娶牌,受9級特大地震影響奔浅,放射性物質發(fā)生泄漏。R本人自食惡果不足惜裙戏,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一乘凸、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧累榜,春花似錦营勤、人聲如沸灵嫌。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽寿羞。三九已至,卻和暖如春赂蠢,著一層夾襖步出監(jiān)牢的瞬間绪穆,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工虱岂, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留玖院,地道東北人。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓第岖,卻偏偏與公主長得像难菌,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蔑滓,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內容

  • Spring Cloud為開發(fā)人員提供了快速構建分布式系統(tǒng)中一些常見模式的工具(例如配置管理郊酒,服務發(fā)現,斷路器键袱,智...
    卡卡羅2017閱讀 134,599評論 18 139
  • 單例模式 適用場景:可能會在場景中使用到對象燎窘,但只有一個實例,加載時并不主動創(chuàng)建蹄咖,需要時才創(chuàng)建 最常見的單例模式褐健,...
    Obeing閱讀 2,056評論 1 10
  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法比藻,內部類的語法铝量,繼承相關的語法,異常的語法银亲,線程的語...
    子非魚_t_閱讀 31,581評論 18 399
  • 工廠模式類似于現實生活中的工廠可以產生大量相似的商品慢叨,去做同樣的事情,實現同樣的效果;這時候需要使用工廠模式务蝠。簡單...
    舟漁行舟閱讀 7,718評論 2 17
  • 《ijs》速成開發(fā)手冊3.0 官方用戶交流:iApp開發(fā)交流(1) 239547050iApp開發(fā)交流(2) 10...
    葉染柒丶閱讀 5,073評論 0 7