关于web端的上传示例官方有两个示例,一个是服务端签名后前端上传的,具体流程如下:
示例地址:https://help.aliyun.com/document_detail/31926.html
示例代码下载:http://gosspublic.alicdn.com/web-upload/oss-h5-upload-js-php.zip
一个为前端直接签名然后上传,具体流程如下:
示例地址:https://help.aliyun.com/document_detail/31925.html
两种上传方式的区别就是前端直接签名上传所用的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而不借用任何功能性插件,整个实现流程也比较清晰,但是这种方法仅仅推荐在个人研究的时候使用,实际工作场景中还是推荐使用插件来上传,因为插件增加了很多功能,像是各个浏览器的兼容性,图片的预览,图片的压缩,多文件上传的支持等等。
参考文档
- XMLHttpRequest Level 2 使用指南:http://www.ruanyifeng.com/blog/2012/09/xmlhttprequest_level_2.html
- plupload中文文档:http://chaping.github.io/plupload/doc/
微信扫描下方的二维码阅读本文