美文网首页服务器配置学习程序员
【Mark】服务端签名直传并设置上传回调

【Mark】服务端签名直传并设置上传回调

作者: 勿忘心安0521 | 来源:发表于2017-08-02 10:33 被阅读252次


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

    相关文章

      网友评论

        本文标题:【Mark】服务端签名直传并设置上传回调

        本文链接:https://www.haomeiwen.com/subject/jijelxtx.html