这段时间在工作中碰到一个需要在h5裁剪图像,然后直传阿里云的需求。图中遇到了一些小问题,分享出来大家都看看。
h5裁剪图像:cropper.js是一个神器啊关于用法,网上可以收罗出大量的帖子,这里我就不多说了,大致原理就是利用canvas裁剪图像然后生成base64的图像(canvas.toDataURL('image/jpeg'))。
转换Blob对象:接下来就是重点了,上传图片我们比较传统的做法就是将 canvas.toDataURL('image/jpeg') 传到后台,然后后台再生成图片传到阿里云。
这样就显得比较繁琐了,怎么将 canvas.toDataURL('image/jpeg')直接传到阿里云?这就懵逼了。这里不得不提到js的另一个东西,Blob对象。
file对象大家都不陌生了,那这个blob又是个什么鬼呢?而实际上 file 对象只是 blob 对象的一个更具体的版本,blob 存储着大量的二进制数据,并且 blob 的 size 和 type 属性,都会被 file 对象所继承。
所以, 在大多数情况下,blob 对象和 file 对象可以用在同一个地方,例如,可以使用 FileReader 借口从 blob 读取数据,也可以使用 URL.createObjectURL() 从 blob 创建一个新的 URL 对象。
这里我先用了canvas一个最简单的办法,创建blob。canvas.toBlob(callback,type,quality);我们可以直接把上传图片的方法写到回调里面,type:类型(图片:‘image/jpeg’),quality:图片质量(0-1).
当然这么高级的方法肯定会涉及到兼容性问题。那我们就回到blob对象的问题上来吧。在chrome早先版本和目前的android中,至少是andrid5.1之前的浏览器,包括微信浏览器等,都不支持blob的构造方法,需要使用BlobBuilder。
var file=new Blob( [array.buffer], {type : "image/jpeg"});//会存在兼容性问题。这个blob的构造方法,在ios手机浏览器是支持的,但是在android手机浏览器不行。
桌面版的chrome浏览器解决了这个blob bug, 但是android手机还是有这个问题,会返回一个type error,因为android浏览器不支持这个构造方法。你必须使用老版本的BlobBuilder API.
解决办法:
//首先要将dataURL转换为Uint8Array对象: function dataURLtoBlob(dataurl) { var arr = dataurl.split(','), mime = arr[0].match(/:(.*?);/)[1], bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n); while(n--){ u8arr[n] = bstr.charCodeAt(n); } return u8arr ; } // 接下来的事情就好办多了,来一发兼容性blob写法: try{ var jpeg = new Blob( [array], {type : "image/jpeg"}); } catch(e){ // TypeError old chrome and FF window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder || window.MSBlobBuilder; if(e.name == 'TypeError' && window.BlobBuilder){ var bb = new BlobBuilder(); bb.append([array.buffer]); var jpeg = bb.getBlob("image/jpeg"); } else if(e.name == "InvalidStateError"){ // InvalidStateError (tested on FF13 WinXP) var jpeg = new Blob( [array.buffer], {type : "image/jpeg"}); } else{ // We're screwed, blob constructor unsupported entirely } }
上传到阿里云:
我查了一下阿里云的web直传的Js示例代码,感觉写的有点复杂,还是用ajax来解决这个问题吧,还是先上代码再来大致讲一下这个问题吧。
Utils.ajax({ url: url1,//url1是先向后台请求上传阿里云的一些参数接口 success: function (res) { if (res.errorCode == 0) { var ossData=new FormData(); ossData.append("OSSAccessKeyId",res.data.accessId);//Bucket 拥有者的Access Key Id。 ossData.append("policy",res.data.policy);//policy规定了请求的表单域的合法性 ossData.append("Signature",res.data.signature);//根据Access Key Secret和policy计算的签名信息,OSS验证该签名信息从而验证该Post请求的合法性 //---以上都是阿里的认证策略 ossData.append("key",res.data.dir+'/'+res.data.filename+'.jpg');//文件名字,可设置路径 ossData.append("success_action_status",'200');// 让服务端返回200,不然,默认会返回204 ossData.append('file', jpeg);//需要上传的文件 file 这个 //接下来就是上传阿里云了的代码了 //ossData.append("callback",callbackbody);//回调,非必选,可以在policy中自定义 $.ajax({ url : res.data.host, //上传阿里地址 data : ossData, processData: false,//默认true,设置为 false,不需要进行序列化处理 cache: false,//设置为false将不会从浏览器缓存中加载请求信息 async: false,//发送同步请求 contentType: false,//避免服务器不能正常解析文件---------具体的可以查下这些参数的含义 //dataType: 'JSON',//不涉及跨域 写json即可 type : 'post', success : function(callbackHost, request) {}, //callbackHost:success,request中就是 回调的一些信息,包括状态码什么的 error : function() { alert("图片上传出错!") } }); } } });
其实Utils.ajax和$.ajax都是一样的,为了区分我这样写的,Utils.ajax的请求是为了得到后台的验证。上传阿里云的一些证书啊,验证码什么的。$.ajax就是上传到阿里云的请求。值得注意点是:
ossData.append('file', jpeg);//需要上传的文件 file 这个jpeg就是我们前面创建的blob。一般上传到阿里云成功后如果不返回任何东西,就不要写入dataType了。
过程大概就是这样,参考了几个人的文章推荐给大家:https://www.cnblogs.com/flicat/p/5337054.html TypeArray、ArrayBuffer、Blob、File、DataURL、canvas的相互转换
http://blog.csdn.net/u012811805/article/details/53885008 移动端UC浏览器不支持Blob的解决方案
https://www.cnblogs.com/mottled/p/6979536.html 阿里OSS ajax方式 web直传