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

关于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

示例代码下载: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而不借用任何功能性插件,整个实现流程也比较清晰,但是这种方法仅仅推荐在个人研究的时候使用,实际工作场景中还是推荐使用插件来上传,因为插件增加了很多功能,像是各个浏览器的兼容性,图片的预览,图片的压缩,多文件上传的支持等等。

参考文档

转载说明

本站所有文章均为原创,转载请注明出处


Eveningme

https://evening.me

强大 自信