公司让我使用阿里云OSS服务器,上传其实很快就搞定了,但是一直搞不懂我上传上去的文件如何下载,翻阅了好多好多资料和文档都没法解决,直到我打了客服电话,虽然并没有解决,但是在解决的路上看到了答案,给我气的。阿里云服务器的文档写的太不友好了,对于我这样的新手使用阿里云服务器的人来说。
背景
请参考Web端直传实践里的背景介绍。
当采用服务端签名后直传方案后,问题来了,用户上传数据后,很多场景下,应用服务器都要知道用户上传了哪些文件,文件名字,甚至如果是图片的话,图片的大小等。为此OSS开发了上传回调功能。
用户的请求逻辑
.用户向应用服务器取到上传policy和回调设置。
.应用服务器返回上传policy和回调。
.用户直接向OSS发送文件上传请求。
.等文件数据上传完,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)发送给应用服务器。
设置方法:修改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往这个机器发送的url请求。
callbackHost: OSS发送这个请求时,请求头部所带的Host头。
callbackBody: OSS请求时,发送给应用服务器的内容,可以包括文件的名字、大小、类型,如果是图片可以是图片的高度、宽度。
callbackBodyType:请求发送的Content-Type。
回调应用服务器
在用户的请求逻辑中,很重要的地方就是第4步和第5步,OSS与应用服务器交互的时候,用户可能会有以下疑问:
问题1:如果我是开发者,那么我要怎么样确认请求是从OSS发送过来的呢?
答案:OSS发送请求时,会跟应用服务器构造签名。两者通过签名保证。
问题2:这个签名是怎么做的?或者有示例代码吗?
答案:有的。上面的例子里面是Callback应用服务器的例子:http://oss-demo.aliyuncs.com:23450(目前只支持Linux)。
运行方案:在Linux下面直接执行里面的文件python callback_app_server.py即可,程序自实现了一个简单的http server,运行该程序可能需要安装rsa的依赖。
问题3:为何我的应用服务器收到的回调请求没有Authotization头?
答案:有些Web server会将Authorization头自行解析掉,比如apache2,因此需要设置成不解析这个头部。以apache2为例,具体设置方法为:
i.打开rewrite模块,执行命令: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();
网友评论