前端文件直传阿里云oss 非plupload插件方式

关于web端的上传示例官方有两个示例,一个是服务端签名后前端上传的,具体流程如下:

前端文件直传阿里云oss 非plupload插件方式插图

示例地址:https://help.aliyun.com/document_detail/31926.html

示例代码下载:http://gosspublic.alicdn.com/web-upload/oss-h5-upload-js-php.zip

一个为前端直接签名然后上传,具体流程如下:

前端文件直传阿里云oss 非plupload插件方式插图1

示例地址:https://help.aliyun.com/document_detail/31925.html

示例代码下载:https://docs-aliyun.cn-hangzhou.oss.aliyun-inc.com/internal/oss/0.0.4/assets/sample/oss-h5-upload-js-direct.zip

两种上传方式的区别就是前端直接签名上传所用的AccessKeyID、AccessKeySecret是直接暴露在前端的,然后通过前端来签名的,这种方法并不安全,仅供纯前端开发者熟悉上传流程使用。

这样纯前端签名上传的示例流程已经非常的明了,像是怎么生成policy,怎么生成signature,怎么构建上传数据都写的很明白,但是还有一处流程不是太清晰的就是示例里用到了一个plupload的插件,插件具体实现了哪些功能,是怎么封装数据的,是不是必须要用插件,能够不用插件直接上传到阿里云oss么,其实可以,下面来整理几个概念,从头捋一下怎么不用插件上传到阿里云oss的流程。

oss的设置

创建Bucket之后需要为Bucket添加跨域策略,步骤为基础设置-跨域设置-创建规则,这里需要有两个必填项,一个是来源,就是哪些域名可以跨域上传到这个Bucket,可以设置多个域名,每行一个域名,也可以为了简单,直接填写*来匹配所有来源即可。还有个是允许跨域的方法,比如get、post之类的,方便起见也可以全部勾选上。

AccessKey

上传到oss需要用到阿里云账号的AccessKey,AccessKey是访问所有阿里云api的密钥,AccessKey分为id和secret两部分,可以到阿里云的用户管理里找到。

前端签名方式

签名后会获得一个policy和signature,具体实现见代码部分,这里用到了两个js库,一个为base64,一个为crypto,crypto的下载地址如下:

https://code.google.com/archive/p/crypto-js/

上传方式:XMLHttpRequest2

因为需要从自己的页面直接上传到阿里云的服务器上,这里需要用到跨域,前面oss端已经设置了可跨域,前端页面也需要支持跨域,所以需要用到XMLHttpRequest2,可能平白无故的看到XMLHttpRequest2这个词有点陌生,但是仔细一想原生ajax的写法无非就是
[cce_js]new XMLHttpRequest();[/cce_js]
这里用到的level2的版本,使用方法还是
[cce_js]new XMLHttpRequest();[/cce_js]
只是根据浏览器对h5的支持,而添加了很多新的功能,像是跨域,超时事件,FormData数据格式的支持等等。

数据格式:FormData

前面说了用XMLHttpRequest2就可以跨域上传文件,那么用什么数据格式来提交呢?这里需要用到的是h5新增的FormData对象,FormData可以模拟表单的数据。使用方法如下:

var formData = new FormData();
formData.append('key', 'value');

那我们怎么确定我们需要上传那些数据呢,这时候我们可以看一下官方的demo都发送了哪些字段:

------WebKitFormBoundaryb4qeA9xbp2qigOux
Content-Disposition: form-data; name="Filename"

oss-h5-upload-js-direct.zip
------WebKitFormBoundaryb4qeA9xbp2qigOux
Content-Disposition: form-data; name="key"

demo/oss-h5-upload-js-direct.zip
------WebKitFormBoundaryb4qeA9xbp2qigOux
Content-Disposition: form-data; name="policy"

eyJleHBpcmF0aW9uIjoiMjAxOS0wMS0wMVQxMjowMDowMC4wMDBaIiwiY29uZGl0aW9ucyI6W1siY29udGVudC1sZW5ndGgtcmFuZ2UiLDAsMTA0ODU3NjAwMF1dfQ==
------WebKitFormBoundaryb4qeA9xbp2qigOux
Content-Disposition: form-data; name="OSSAccessKeyId"

6MKOqxGiGU4AUk44
------WebKitFormBoundaryb4qeA9xbp2qigOux
Content-Disposition: form-data; name="success_action_status"

200
------WebKitFormBoundaryb4qeA9xbp2qigOux
Content-Disposition: form-data; name="signature"

rpLzvv3NVi4btaft7bvwmVI5Qtg=
------WebKitFormBoundaryb4qeA9xbp2qigOux
Content-Disposition: form-data; name="file"; filename="oss-h5-upload-js-direct.zip"
Content-Type: application/zip

------WebKitFormBoundaryb4qeA9xbp2qigOux--

总共7个字段,分别是:

  • Filename:文件名
  • key:上传路径+文件名
  • policy:通过base64加密过的上传时间限制和文件大小限制的json数据
  • OSSAccessKeyId:前面有提到的AccessKeyID
  • success_action_status:代表成功的状态码
  • signature:签名
  • file:文件对象

这里有一个需要注意的地方是FormData的字段顺序必须按照这7个字段的排列顺序来,不然的话会导致上传不成功。

上传地址

在Bucket的概览里外网访问地址,就是上传地址,上传后的访问地址为外网地址+文件夹名+文件名

代码实现

具体实现代码如下:

<!DOCTYPE html>
<html>

<head>
  <title>前端直传oss-非插件</title>
</head>

<body>
  <input type="file" name="file">
  <p id="tip">
  </p>
  <script type="text/javascript" src="./scripts/jquery-3.2.1.min.js"></script>
  <script type="text/javascript" src="./scripts/crypto1/crypto/crypto.js"></script>
  <script type="text/javascript" src="./scripts/crypto1/hmac/hmac.js"></script>
  <script type="text/javascript" src="./scripts/crypto1/sha1/sha1.js"></script>
  <script type="text/javascript" src="./scripts/base64.js"></script>
  <script type="text/javascript">
    //以下三项想要完整使用时需要更换成自己的AccessKeyID、AccessKeySecret和上传地址
    //AccessKeyID
    accessid = '6MKOqxGiGU4AUk44';
    //AccessKeySecret
    accesskey = 'ufu7nS8kS59awNihtjSonMETLI0KLy';
    //上传地址
    host = 'http://post-test.oss-cn-hangzhou.aliyuncs.com';
    var policyText = {
      "expiration": "2019-01-01T12:00:00.000Z", //设置该Policy的失效时间,超过这个失效时间之后,就没有办法通过这个policy上传文件了
      "conditions": [
        ["content-length-range", 0, 1048576000] // 设置上传文件的大小限制
      ]
    };
    //执行加密
    var policyBase64 = Base64.encode(JSON.stringify(policyText));
    message = policyBase64;
    var bytes = Crypto.HMAC(Crypto.SHA1, message, accesskey, {
      asBytes: true
    });
    var signature = Crypto.util.bytesToBase64(bytes);
    //上传代码开始
    $('body').on("change", "input[type='file']", function (e) {
      var _self = this;
      //构造表单数据
      var formData = new FormData();
      formData.append('Filename', _self.files[0]['name']);
      formData.append('key', 'demo/' + _self.files[0]['name']);
      formData.append('policy', policyBase64);
      formData.append('OSSAccessKeyId', accessid);
      formData.append('success_action_status', '200');
      formData.append('signature', signature);
      formData.append('file', _self.files[0]);
      //创建xhr对象 
      var xhr = new XMLHttpRequest();
      //设置xhr请求的超时时间
      xhr.timeout = 3000;
      //设置响应返回的数据格式
      xhr.responseType = "json";
      //创建一个 post 请求,采用异步
      xhr.open('POST', host, true);
      //注册相关事件回调处理函数
      xhr.onload = function (e) {
        if (this.status == 200 || this.status == 304) {
          $("#tip").html('<a href="' + host + '/demo/' + _self.files[0]['name'] + '" target="_blank">下载地址为:' +
            host + '/oss-demo/' + _self.files[0]['name'] + '</a>');
        }
      };
      xhr.ontimeout = function (e) {
        alert("上传超时!")
      };
      xhr.onerror = function (e) {
        alert("上传错误!")
      };
      xhr.upload.onprogress = function (e) {
        $("#tip").html('上传进度:' + (e.loaded / e.total).toFixed(2) * 100 + '%');
      };
      //发送数据
      xhr.send(formData);
    })
  </script>
</body>

</html>

完整demo下载地址:http://eveningme.oss-cn-hangzhou.aliyuncs.com/oss-demo.zip

总结

通过完整示例可以看出仅需要短短几行代码就能实现把文件上传到oss而不借用任何功能性插件,整个实现流程也比较清晰,但是这种方法仅仅推荐在个人研究的时候使用,实际工作场景中还是推荐使用插件来上传,因为插件增加了很多功能,像是各个浏览器的兼容性,图片的预览,图片的压缩,多文件上传的支持等等。

参考文档



微信扫描下方的二维码阅读本文

前端文件直传阿里云oss 非plupload插件方式插图2

Add comment

By Eveningme