最近在做一个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图片的问题吗?欢迎来访啊