Web端直传实践

转载自:https://help.aliyun.com/document_detail/oss/practice/post_object_callback.html?spm=5176.docoss/user_guide/upload_object/simple_upload.2.12.NDyTzZ

Web端直传实践

目的

本教程的目录是通过三个例子介绍如何在Html表单提交直传OSS

  • 第一个例子:讲解签名在客户端(Javascript)完成,然后直接通过表单上传到OSS, 注意这个例子有安全风险,推荐使用第二个例子和第三个例子
  • 第二个例子:讲解签名在服务端(php)完成,然后直接通过表单上传到OSS
  • 第三个例子:讲解签名在服务端(php)完成, 并且服务端面设置了上传后回调。然后直接通过表单上传到OSS,OSS回调完应用服务器再返回给用户。

背景

每个用OSS的用户,都会用到上传。由于是网页上传,其中包括一些APP里面的h5页面,对上传的需求很强烈,很多人采用的做法是用户在浏览器/APP上传到应用服务器,然后应用服务器再把文件上传到OSS。

这种方法有三个缺点:

  • 第一:上传慢,先上传到应用服务器,再上传到OSS,网络传送多了一倍,而且OSS是采用BGP带宽,能保证各地各运营商的速度。
  • 第二:扩展性不好,如果后续用户多了,应用服务器会成为瓶颈。
  • 第三:费用高,因为OSS上传流量是免费的。如果数据直传到OSS,不走应用服务器。那么将能省下几台应用服务器。

基础篇:客户端用JS直接签名,然后上传到OSS

示例

下面我将介绍用plupload ,在JS端签名然后直传数据到OSS的例子 用户电脑浏览器测试样例:http://oss-demo.aliyuncs.com/oss-h5-upload-js-direct/index.html

用手机测试该上传是否有效。二维码:可以用手机(微信,QQ,手机浏览器等)扫一扫试试(这个不是广告,只是上述网址的二维码。这为了让大家看一下这个实现能在手机端完美运行。)

文件上传是上传到一个测试的公共 bucket , 会定时清理,所以不要传一些敏感及重要数据。

代码下载

点击这里:oss-h5-upload-js-direct.tar.gz

原理

本例子的功能
  1. 采用plupload 直接提交表单数据(即PostObject)到OSS
  2. 支持html5,flash,silverlight,html4 等协议上传
  3. 可以运行在PC浏览器,手机浏览器,微信等
  4. 可以选择多文件上传
  5. 显示上传进度条
  6. 可以控制上传文件的大小

OSS的PostObject API细节可以参照这里(看不懂没有关系)。

plupload

plupload是一款简单易用且功能强大, 拥有多种上传方式,(html5, flash, silverlight, html4)等方式,会智能检测当前环境 选择最适合的方式,并且会优先采用Html5, 所以不用花心思去当前的浏览器要用何种方式上传,plupload会帮您考虑好。

关键代码

因为OSS原生支持POST协议。所以只要将plupload在发送POST请求时,带上OSS签名即可。 核心代码如下:

var uploader = new plupload.Uploader({
    runtimes : 'html5,flash,silverlight,html4',
    browse_button : 'selectfiles',
    //runtimes : 'flash',
    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 : host,
    multipart_params: {
        'Filename': '${filename}',
        'key' : '${filename}',
        'policy': policyBase64,
        'OSSAccessKeyId': accessid,
        'success_action_status' : '200', //让服务端返回200,不然,默认会返回204
        'signature': signature,
    },
 ....
}

签名signature主要是对policyText进行签名,最简单的例子如下:

var policyText = {
    "expiration": "2020-01-01T12:00:00.000Z", //       设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了
    "conditions": [
    ["content-length-range", 0, 1048576000] // 设置上传文件的大小限制,如果超过了这个大小,文件上传到OSS会报错的
    ]
}
CORS

注意:如果一定要保证bucket属性CORS设置支持POST方法。因为这个HTML直接上传到OSS,会产生跨域请求。必须在bucket属性里面设置允许跨域

设置如下图:

进阶篇:应用服务器php返回签名

上述例子有一个很严重的安全隐患。就是OSS AccessId/AccessKey暴露在前端页面。可以随意拿到accessid/accesskey. 这是非常不安全的做法。 将此例子进化,签名及上传policy从后端php代码取。

请求逻辑是:

  1. 客户端要上传图片时,到应用服务器取上传的policy及签名
  2. 客户端拿到签名直接上传到OSS

示例

直接用网页访问:http://oss-demo.aliyuncs.com/oss-h5-upload-js-php/index.html

用手机测试该上传是否有效。二维码:可以用手机(微信,QQ,手机浏览器等)扫一扫试试(这个不是广告,只是上述网址的二维码。这为了让大家看一下这个实现能在手机端完美运行。)

代码下载

点击这里:oss-h5-upload-js-php.tar.gz

例子是采用后端签名语言是用PHP,采用Java语言后端签名的示例是:点击这里

原理

设置plupload 上传参数如下:

multipart_params: {
    'key' : key + '${filename}' //后面会介绍到,key是应用服务器返回的,指定用户必须以这个前缀上传文件。
    'policy': policyBase64,
    'OSSAccessKeyId': accessid,
    'success_action_status' : '200', //让服务端返回200,不然,默认会返回204
    'signature': signature,
},

js最主要是从后端取到policyBase64, 及accessid,及signature这三个变量。 往后端取这三个变量核心代码如下:

phpUrl = './php/get.php'
        xmlhttp.open( "GET", phpUrl, false );
        xmlhttp.send( null );
        var obj = eval ("(" + xmlhttp.responseText+ ")");
        host = obj['host']
        policyBase64 = obj['policy']
        accessid = obj['accessid']
        signature = obj['signature']
        expire = parseInt(obj['expire'])
        key = obj['dir']

现在咱们来一起解析一下xmlhttp.responseText(这个是我设计的范围,并不一定要求是以下的格式,但是必须有signature, accessid, policy这三个值)

{"accessid":"6MKOqxGiGU4AUk44",
"host":"http://post-test.oss-cn-hangzhou.aliyuncs.com",
"policy":"eyJleHBpcmF0aW9uIjoiMjAxNS0xMS0wNVQyMDoyMzoyM1oiLCJjxb25kaXRpb25zIjpbWyJjcb250ZW50LWxlbmd0aC1yYW5nZSIsMCwxMDQ4NTc2MDAwXSxbInN0YXJ0cy13aXRoIiwiJGtleSIsInVzZXItZGlyXC8iXV19",
"signature":"I2u57FWjTKqX/AE6doIdyff151E=",
"expire":1446726203,"dir":"user-dir/"}

第一个变量accessid: 指的用户请求的accessid,注意单知道accessid, 对数据不会有影响。

第二个变量host: 指的是用户要往哪个域名发往上传请求。

第三个变量policy:指的是用户表单上传的策略policy, 是经过base64编码过的字符串

第四个变更signature:是对上述第三个变量policy签名后的字符串 第五个变量expire:指的是当前上传策略失效时间,这个变量,并不是用来发送到OSS,因为这个已经指定在policy里面,这个变量的含义,后面讲。

现在咱们分析一下policy的内容,将其解码后的内容是:

{"expiration":"2015-11-05T20:23:23Z",
"conditions":[["content-length-range",0,1048576000],
["starts-with","$key","user-dir/"]]

这里有一个关键的地方,PolicyText指定了该Policy 上传失效的最终时间。即在这个失效时间之前,都可以利用这个policy上传文件,所以没有必要每次上传,都去后端取签名。减少后端的压力。在这里我的设计是:初始化上传时,每上传一个文件后,取一次签名。然后再上传时,将当前时间跟签名时间对比,看是签名时间是否失效了。如果失效了,就重新取一次签名,如果没有失效就不取。这里就用到了第五个变量expire 核心代码如下:

now = timestamp = Date.parse(new Date()) / 1000;
[color=#000000]//可以判断当前expire是否超过了当前时间,如果超过了当前时间,就重新取一下.3s 做为缓冲[/color]
    if (expire < now + 3)
{  
     ..... 
     phpUrl = './php/get.php'
     xmlhttp.open( "GET", phpUrl, false );
     xmlhttp.send( null );
     ......
}
return .

再看一下上面policy 的内容比上面增加了starts-with, 这个指定此次上传的文件名,必须是user-dir开头(这个字符串,用户可以自己指定)

为什么要增加这个的含义是:很多场景,一个应用一个bucket,不同用户的数据,为了防止数字覆盖,每个人上传到OSS,可以有特定的前缀。那么问题来了,那用户获取到这个policy后,是不是在失效期内,都能修改上传前缀,从而上传到别人的目录呢?所以,应用服务器可以在上传时就指定让用户传文件时,必须是某个前缀。如果用户拿到了policy他也没有办法上传别人的前缀上。保证了数据的安全性。

终级篇--应用服务器php返回签名及采用上传回调

当采用第二个方案后,问题来了,用户来了数据,并且上传数据后,很多场景下,应用服务器都要知道用户上传了哪些文件,文件名字,甚至如果是图片的话,图片的大小等。为此OSS开发了上传回调功能。

上传回调功能目前大陆区域都已经支持。

用户的请求逻辑

  • 第一:用户先向应用服务器取到上传policy和回调设置
  • 第二:应用服务器返回上传policy和回调
  • 第二:用户直接向OSS发送文件上传请求
  • 第三:等文件数据上传完,OSS给用户Response前,OSS会根据用户的回调设置,请求用户的服务器。
  • 第四:如果应用服务器返回成功,那么就返回用户成功,如果应用服务器返回失败,那么OSS也返回给用户失败。这样确保了用户上传成功的照片,应用服务器都已经收到通知了。
  • 第五:应用服务器给OSS返回。
  • 第六:OSS将应用服务器返回的内容返回给用户。

示例

示例:http://oss-demo.aliyuncs.com/oss-h5-upload-js-php-callback/index.html

用手机测试该上传是否有效。二维码:可以用手机(微信,QQ,手机浏览器等)扫一扫试试(这个不是广告,只是上述网址的二维码。这为了让大家看一下这个实现能在手机端完美运行。)

代码要添加的东西

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

代码下载

点击这里:oss-h5-upload-js-php-callback.tar.gz

例子是采用后端签名语言是用PHP,采用Java语言后端签名的示例是:点击这里

回调应用服务器

在上述有一个很重要的地方就是第四步和第五步,OSS与应用服务器交互的时候,

  • 问题1:如果我是开发者,那么我要怎么样确认请求是从OSS发送过来的呢?

    答案:OSS发送请求时,会跟应用服务器构造签名。两者通过签名保证。

  • 问题2:这个签名是怎么做的?或者有示例代码吗?

    答案:有的。上面的例子里面是Callback应用服务器的例子是:http://oss-demo.aliyuncs.com:23450 (目前只支持linux) 上面运行的代码是:点击这里:callback_app_server.py.zip 运行方案,在linux下面直接执行里面的文件: python callback_app_server.py即可, 程序自实现了一个简单的http server,运行该程序可能需要安装rsa的依赖

  • 问题3: 为何我的应用服务器收到的回调请求没有Authotization头

    答案:有些Webserver会将Authorization头自行解析掉,比如apache2,因此需要设置成不解析这个头部。 以apache2为例,具体设置方法为: 1、打开rewrite模块,执行命令:a2enmod rewrite; 2、修改配置文件/etc/apache2/apache2.conf(apache2的安装路径不同会有不一样), 将Allow Override设置成All,然后添加下面两条配置: RewriteEngine on
    RewriteRule .* - [env=HTTP_AUTHORIZATION:%{HTTP:Authorization},last]

示例程序只是完成了如何检查应用服务器收到的签名, 用户要自行增加对应用服务器收到回调的内容的格式解析 。

其他语言的回调应用服务器版本

Java版本:

  • 下载地址:点击这里
  • 运行方法,解压包运行java -jar oss-callback-server-demo.jar 9000(9000就运行的端口,可以自己指定)

    注意这个jar例子在java 1.7运行通过,如果有问题可以自己依据提供的代码进行修改。这是一个maven项目

PHP版本:

  • 下载地址:点击这里
  • 运行方法:部署到Apache环境下,因为PHP本身语言的特点,取一些数据头部会依赖于环境。所以可以参考例子基于自己所在环境进行修改

Python版本:

  • 下载地址:点击这里
  • 运行方法:解压包直接运行python callback_app_server.py即可,程序自实现了一个简单的http server,运行该程序可能需要安装rsa的依赖。

Ruby版本:

  • 下载地址:点击这里
  • 运行方法: ruby aliyun_oss_callback_server.rb

总结


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值