最近在做一个vue移动端的项目,设计技术为vue2+vue相关+vant-ui+less,搭配浙里办Bridge插件。也算是几年来第一次做移动端相关的项目了,在做的过程中,记录下几个问题,一起分享下我的解决方式。
在项目中有两处下载图片功能,一处为将base64格式的二维码图片下载到本地,一处为将html页面生成图片下载到本地。在最开始做的时候,我没想到手机端会有问题,直接按照pc浏览器上下载文件的方式去完成的,效果也出来了。
pc端下载的原理还是比较易于理解的,将base64格式的图片转化为blob对象,再利用document对象创建一个a标签,通过点击方法实现图片下载,代码如下(代码非原创):
downloadFile(url){ let aLink = document.createElement('a'); let blob = this.base64ToBlob(url); //new Blob([content]); let evt = document.createEvent("HTMLEvents"); console.log("点击下载",evt) evt.initEvent("click", true, true);//initEvent 不加后两个参数在FF下会报错 事件类型,是否冒泡,是否阻止浏览器的默认行为 aLink.download = 'xxx.png'; aLink.href = URL.createObjectURL(blob); aLink.click() }, base64ToBlob(code) { let parts = code.split(';base64,'); console.log(parts); let contentType = parts[0].split(':')[1]; let raw = window.atob(parts[1]); let rawLength = raw.length; let uInt8Array = new Uint8Array(rawLength); for (let i = 0; i < rawLength; ++i) { uInt8Array[i] = raw.charCodeAt(i); } return new Blob([uInt8Array], {type: contentType}); }
这样子,PC上的效果是出来了,点击下载按钮执行方法后可以把图片下载到电脑上,但是没想到等项目到真机测试的时候,点击下载按钮是没有效果的。
真的是一头雾水,pc上还好好的,手机上咋就不行了呢!
最开始以为是方法不对,在网站上一顿搜索,试遍了找到的各种方法,还是没有用。又以为是手机端浏览器的问题,装了vconsole去查看手机端浏览器信息,也没有发现有啥异常的地方。
最后在SegmentFault思否网站上的一个贴子力看到了类似的问题,有人在下边评论里说了一句:手机端浏览器禁止下载base64格式的文件,这才明白过来。
下面的回复也有人说没发现有解决方案,最后和产品经理商量后被迫砍掉了下载功能。我是真的佩服!!!
因为项目里要使用浙里办插件,里面有完整的下载图片的API,但文档里也是特备注明了只支持下载网络资源图片。
综合以上信息:
好比是金箍棒,两头都是好的,就中间部分断了,该怎么接上呢?
重点就是如何才能把base64文件转成网络资源图片?
当时项目已经做了大半,前期的功能已经比较完善了,后端提供的上传接口是File文件格式,提交数据时候需要使用FormData对象包裹起来,那如果我把base64格式文件转成File格式对象,上传到服务器之后拿到网路资源地址,不就可以下载了吗?
说干就干,网上一搜索,还真的有把base64转成File对象的方法。
/* 分为两步: * 1.base64转为blob对象, * 2.blob对象转为file对象 */ baseToBlob(url) { var arr = url.split(','); var mime = arr[0].match(/:(.*?);/)[1]; var bstr = atob(arr[1]); var n = bstr.length; var u8arr = new Uint8Array(n); while (n--) { u8arr[n] = bstr.charCodeAt(n) } return new Blob([u8arr], { type: mime }) }, blobToFile(theBlob, fileName){ let files = new File([theBlob], fileName, {type: 'image/jpeg'}) theBlob.lastModifiedDate = new Date(); theBlob.name = fileName; return files; },
这样解决办法有很多帖子都是这么写的,可能适用于他们的情况,但无语我这里确是不行。因为上传接口的规定是必须是File格式的对象,依照以上代码转换出来的对象依旧是Blob格式,上传失败。
又在经历了很长时间的搜索之后,我突然发现了一个文章上的不同点,在baseToBlob方法的最后一行代码中可以将new Blob直接改为new File。那种感觉怎么说呢?就是你可以预想到这个一定是正确的,哈哈
果然,一试就成功上传,拿到了网络资源的地址。
downloadFile(){ let content = this.qrCode; let blob = this.dataURLtoBlob(content,'二维码.png'); let formData = new FormData() formData.append("file",blob) upload(formData).then(res=>{ let {data} = res.data if(data && data.url){ // xxx } }) }, dataURLtoBlob: function(dataurl,fileName) { 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 new File([u8arr],fileName, { type: mime }); },
功能顺利完成,虽然经过上传接口过了一手,下载完成的速度有点慢,但是功能效果是完好的。各位有缘能看到文章的大佬们,能在评论区说下你们是怎么解决移动端下载base64图片的问题吗?欢迎来访啊