最近在做一个vue移动端的项目,设计技术为vue2+vue相关+vant-ui+less,搭配浙里办Bridge插件。也算是几年来第一次做移动端相关的项目了,在做的过程中,记录下几个问题,一起分享下我的解决方式。

下载图片

在项目中有两处下载图片功能,一处为将base64格式的二维码图片下载到本地,一处为将html页面生成图片下载到本地。在最开始做的时候,我没想到手机端会有问题,直接按照pc浏览器上下载文件的方式去完成的,效果也出来了。

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文件但不能下载
  • 浙里办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图片的问题吗?欢迎来访啊

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐