vue实现压缩图片预览并上传功能(promise封装)


Posted in Javascript onJanuary 10, 2019

本文实例为大家分享了vue实现压缩图片预览并上传的具体代码,供大家参考,具体内容如下

主要用到filereader、canvas 以及 formdata 这三个h5的api

过程大致分为三步:

用户使用input file上传图片的时候,用filereader读取用户上传的图片数据(base64格式)
把图片数据传入img对象,然后将img绘制到canvas上,再调用canvas.toDataURL对图片进行压缩
获取到压缩后的base64格式图片数据,转成二进制塞入formdata,再通过XmlHttpRequest提交formdata。

模板:

<template>
 <div class="image-box">
 <input type="file" accept="image/*" @change="imageHandle">
 <img ref="upImg"/>
 </div>
</template>

获取图片数据

methods: {
  //监听input file的change事件
 imageHandle(e) {
  //**这个是必不可少的,在下面的reader.onload中this就不再指vm了**
  let that = this;
  let maxSize = 100 * 1024;
  let files = e.srcElement.files;
  if (!files.length) return; //文件长度大于0
  if (!/^image\//.test(files[0].type)) return; //必须是图片才处理
  if (!window.FileReader) return; //支持FileReader
  //创建filereader对象
  let reader = new FileReader();
  reader.readAsDataURL(files[0]); //将图片转成base64格式
  reader.onload = function() {
  let result = this.result;
  let img = new Image();
  img.src = result;
  let formdata = new FormData();
  if (this.result.length <= maxSize) {
   that.$refs.upImg.src = result; //预览图片
   img = null;
   //上传图片
   formdata.append("image", that._upload(result, files[0].name, files[0].type));
   that.$store.dispatch("uploadImage", formdata)
    .then(data => {
     if (data === 1) {
     that.$toast("上传成功", "success");
     } else if (data === -1) {
     that.$toast("图片为空", "error");
     } else {
     that.$toast("上传失败", "error");
     }
    })
    .catch(error => that.$toast("上传失败", "error"));
  } else {
   img.onload = function() {
   //压缩图片
   let data = that._compress(img);
   //图片预览
   that.$refs.upImg.src = data;
   //上传图片
   formdata.append("image", that._upload(data, files[0].name, files[0].type));
   that.$store.dispatch("uploadImage", formdata)
     .then(data => {
      if (data === 1) {
      that.$toast("上传成功", "success");
      } else if (data === -1) {
      that.$toast("图片为空", "error");
      } else {
      that.$toast("上传失败", "error");
      }
     })
     .catch(error => that.$toast("上传失败", "error"));
   };
  }
  };
 },

压缩图片

在IOS中,canvas绘制图片是有两个限制的:

首先是图片的大小,如果图片的大小超过两百万像素,图片也是无法绘制到canvas上的,调用drawImage的时候不会报错,但是你用toDataURL获取图片数据的时候获取到的是空的图片数据。

再者就是canvas的大小有限制,如果canvas的大小大于大概五百万像素(即宽高乘积)的时候,不仅图片画不出来,其他什么东西也都是画不出来的。

应对第一种限制,处理办法就是瓦片绘制了。瓦片绘制,也就是将图片分割成多块绘制到canvas上,我代码里的做法是把图片分割成100万像素一块的大小,再绘制到canvas上。

而应对第二种限制,我的处理办法是对图片的宽高进行适当压缩,我代码里为了保险起见,设的上限是四百万像素,如果图片大于四百万像素就压缩到小于四百万像素。四百万像素的图片应该够了,算起来宽高都有2000X2000了。

如此一来就解决了IOS上的两种限制了。

除了上面所述的限制,还有两个坑,一个就是canvas的toDataURL是只能压缩jpg的,当用户上传的图片是png的话,就需要转成jpg,也就是统一用canvas.toDataURL(‘image/jpeg', 0.1) , 类型统一设成jpeg,而压缩比就自己控制了。

另一个就是如果是png转jpg,绘制到canvas上的时候,canvas存在透明区域的话,当转成jpg的时候透明区域会变成黑色,因为canvas的透明像素默认为rgba(0,0,0,0),所以转成jpg就变成rgba(0,0,0,1)了,也就是透明背景会变成了黑色。解决办法就是绘制之前在canvas上铺一层白色的底色。

_compress(img) {
  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext("2d");
  //瓦片
  let tCanvas = document.createElement("canvas");
  let tctx = tCanvas.getContext("2d");
  let initSize = img.src.length;
  let width = img.width;
  let height = img.height;
  //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
  let ratio;
  if ((ratio = (width * height) / 4000000) > 1) {
  ratio = Math.sqrt(ratio);
  widht /= ratio;
  height /= ratio;
  } else {
  ratio = 1;
  }
  canvas.width = width;
  canvas.height = height;
  //铺底色
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  //如果图片像素大于100万则使用瓦片绘制
  let count;
  if ((count = (width * height) / 1000000) > 1) {
  count = ~~(Math.sqrt(count) + 1); //计算要分成多少瓦片,~~在这里表示取整
  //计算每块瓦片的宽高
  let nw = ~~(width / count);
  let nh = ~~(height / count);
  tCanvas.width = nw;
  tCanvas.height = nh;
  for (let i = 0; i < count; i++) {
   for (let j = 0; j < count; j++) {
   tctx.drawImage(
    img, i * nw * ratio, j * nh * ratio, nw * ratio,nh * ratio, 0, 0, nw,nh
   );
   ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
   }
  }
  } else {
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  }
  //进行压缩
  let ndata = canvas.toDataURL("image/jpeg", 0.3);
  tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
  return ndata;
 },

上传

完成图片压缩后,就可以塞进formdata里进行上传了,先将base64数据转成字符串,再实例化一个ArrayBuffer,然后将字符串以8位整型的格式传入ArrayBuffer,再通过BlobBuilder或者Blob对象,将8位整型的ArrayBuffer转成二进制对象blob,再将blob转为File对象

_upload(data, name, type) {
  let text = window.atob(data.split(",")[1]);
  let buffer = new ArrayBuffer(text.length);
  let ubuffer = new Uint8Array(buffer);
  let pecent = 0,
  loop = null;

  for (var i = 0; i < text.length; i++) {
  ubuffer[i] = text.charCodeAt(i);
  }

  let Builder =
  window.BlobBuilder ||
  window.WebKitBlobBuilder ||
  window.MozBlobBuilder ||
  window.MSBlobBuilder;
  let blob;
  if (Builder) {
  var builder = new Builder();
  builder.append(buffer);
  blob = builder.getBlob(type);
  } else {
  blob = new window.Blob([ubuffer], { type: type });
  }
  // blob 转file
  var fileOfBlob = new File([blob], name, { type: type });
  return fileOfBlob;
 }
 }

将图片压缩上传封装到一个js文件里

const UploadImg = {
 imageHandle(files, maxSize, imgDom) {
  let that = this;
  let formdata = new FormData();
  let reader = new FileReader();
  reader.readAsDataURL(files[0]); //将图片转成base64格式
  //reader.onload是异步,要用到Promise对象将值返回出去
  return new Promise((resolved, rejected) => {
   reader.onload = function () {
    let result = this.result;
    let img = new Image();
    img.src = result;
    if (this.result.length <= maxSize) {
     imgDom.src = result;
     img = null;
     formdata.append("image", that._upload(result, files[0].name, files[0].type));
     resolved(formdata);
    } else {
     img.onload = function () {
      let data = that._compress(img);
      imgDom.src = data;
      formdata.append("image", that._upload(data, files[0].name, files[0].type));
      resolved(formdata);
     };
    }
   };
  })

 },
 _compress(img) {
  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext("2d");
  //瓦片
  let tCanvas = document.createElement("canvas");
  let tctx = tCanvas.getContext("2d");
  let width = img.width;
  let height = img.height;
  //如果图片大于四百万像素,计算压缩比并将大小压至400万以下
  let ratio;
  if ((ratio = (width * height) / 4000000) > 1) {
   ratio = Math.sqrt(ratio);
   widht /= ratio;
   height /= ratio;
  } else {
   ratio = 1;
  }
  canvas.width = width;
  canvas.height = height;
  //铺底色
  ctx.fillStyle = "#fff";
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  //如果图片像素大于100万则使用瓦片绘制
  let count;
  if ((count = (width * height) / 1000000) > 1) {
   count = ~~(Math.sqrt(count) + 1); //计算要分成多少瓦片
   //计算每块瓦片的宽高
   let nw = ~~(width / count);
   let nh = ~~(height / count);
   tCanvas.width = nw;
   tCanvas.height = nh;
   for (let i = 0; i < count; i++) {
    for (let j = 0; j < count; j++) {
     tctx.drawImage(img, i * nw * ratio, j * nh * ratio, nw * ratio, nh * ratio, 0, 0, nw, nh);
     ctx.drawImage(tCanvas, i * nw, j * nh, nw, nh);
    }
   }
  } else {
   ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
  }
  //进行最小压缩
  let ndata = canvas.toDataURL("image/jpeg", 0.3);
  tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
  return ndata;
 },
 _upload(data, name, type) {
  let text = window.atob(data.split(",")[1]);
  let buffer = new ArrayBuffer(text.length);
  let ubuffer = new Uint8Array(buffer);

  for (var i = 0; i < text.length; i++) {
   ubuffer[i] = text.charCodeAt(i);
  }

  let Builder =
   window.BlobBuilder ||
   window.WebKitBlobBuilder ||
   window.MozBlobBuilder ||
   window.MSBlobBuilder;
  let blob;
  if (Builder) {
   var builder = new Builder();
   builder.append(buffer);
   blob = builder.getBlob(type);
  } else {
   blob = new window.Blob([ubuffer], { type: type });
  }
  // blob 转file
  var fileOfBlob = new File([blob], name, { type: type });
  return fileOfBlob;
 }
}

export default UploadImg

调用代码

import UploadImg from "../../util/uploadImg";

methods: {
 imageHandle(e) {
  let maxSize = 100 * 1024;
  let imgDom = this.$refs.upImg;
  let files = e.srcElement.files;
  if (!files.length) return; //文件长度大于0
  if (!/^image\//.test(files[0].type)) return; //必须是图片才处理
  if (!window.FileReader) return; //支持FileReader

  if (this.docEntry === "" || this.lineId === "") {
  this.$toast("请填写完整信息", "error");
  return;
  }
  // let formdata = new FormData();
  UploadImg.imageHandle(files, maxSize, imgDom).then(formdata => {
  formdata.append("docEntry", this.docEntry);
  formdata.append("lineId", this.lineId);
  formdata.append("action", "ProductionListImage");
  this.$store
   .dispatch("uploadImage", formdata)
   .then(data => {
   if (data === 1) {
    this.$toast("上传成功", "success");
   } else if (data === -1) {
    this.$toast("图片为空", "error");
   } else {
    this.$toast("上传失败", "error");
   }
   })
   .catch(error => this.$toast("上传失败", "error"));
  });
 }
 }

参考链接:移动端利用H5实现压缩图片上传功能

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
Ext.MessageBox工具类简介
Dec 10 Javascript
window.dialogArguments 使用说明
Apr 11 Javascript
js multiple全选与取消全选实现代码
Dec 04 Javascript
ExtJS4 Grid改变单元格背景颜色及Column render学习
Feb 06 Javascript
使用js正则控制input标签只允许输入的值
Jul 29 Javascript
js实现动态改变字体大小代码
Jan 02 Javascript
手机号码,密码正则验证
Sep 04 Javascript
常用的jQuery前端技巧收集
Dec 24 Javascript
JavaScript数组对象实现增加一个返回随机元素的方法
Jul 27 Javascript
如何去除vue项目中的#及其ie9兼容性
Jan 11 Javascript
微信小程序保存多张图片的实现方法
Mar 05 Javascript
js实现点赞按钮功能的实例代码
Mar 06 Javascript
微信小程序地图(map)组件点击(tap)获取经纬度的方法
Jan 10 #Javascript
最简单的JS实现json转csv的方法
Jan 10 #Javascript
puppeteer实现html截图的示例代码
Jan 10 #Javascript
其实你可以少写点if else与switch(推荐)
Jan 10 #Javascript
微信小程序提取公用函数到util.js及使用方法示例
Jan 10 #Javascript
浅谈JavaScript 代码简洁之道
Jan 09 #Javascript
react组件从搭建脚手架到在npm发布的步骤实现
Jan 09 #Javascript
You might like
php实现简单文件下载的方法
2015/01/30 PHP
toString()一个会自动调用的方法
2010/02/08 Javascript
javascript 数组学习资料收集
2010/04/11 Javascript
基于JQuery的asp.net树实现代码
2010/11/30 Javascript
JS/FLASH实现复制代码到剪贴板(兼容所有浏览器)
2013/05/27 Javascript
JavaScript字符串对象split方法入门实例(用于把字符串分割成数组)
2014/10/16 Javascript
js实现点击添加一个input节点
2014/12/05 Javascript
jQuery on方法传递参数示例
2014/12/09 Javascript
JavaScript获取指定元素位置的方法
2015/04/08 Javascript
jQuery实现手机号码输入提示功能实例
2015/04/30 Javascript
最丑的时钟效果!js canvas时钟制作方法
2016/08/15 Javascript
浅析JavaScript动画模拟拖拽原理
2016/12/09 Javascript
jquery.form.js异步提交表单详解
2017/04/25 jQuery
详解vue-router 2.0 常用基础知识点之router-link
2017/05/10 Javascript
nodejs一个简单的文件服务器的创建方法
2019/09/13 NodeJs
javascript 原型与原型链的理解及应用实例分析
2020/02/10 Javascript
vue 使用 canvas 实现手写电子签名
2020/03/06 Javascript
2分钟实现一个Vue实时直播系统的示例代码
2020/06/05 Javascript
Python进程间通信用法实例
2015/06/04 Python
python设置值及NaN值处理方法
2018/07/03 Python
python  ceiling divide 除法向上取整(或小数向上取整)的实例
2019/12/27 Python
python安装sklearn模块的方法详解
2020/11/28 Python
linux系统下pip升级报错的解决方法
2021/01/31 Python
使用CSS3实现一个3D相册效果实例
2016/12/03 HTML / CSS
如何在Canvas上的图形/图像绑定事件监听的实现
2020/09/16 HTML / CSS
狗狗玩具、零食和咀嚼物的月度送货服务:Super Chewer
2018/08/22 全球购物
标准导师推荐信(医学类)
2013/10/28 职场文书
个人自我评价分享
2013/12/20 职场文书
三年级语文教学反思
2014/02/01 职场文书
物流专员岗位职责
2014/02/17 职场文书
电脑售后服务承诺书
2014/03/27 职场文书
委托代理人授权委托书范本
2014/09/24 职场文书
中小学教师继续教育心得体会
2016/01/19 职场文书
公司趣味运动会开幕词
2016/03/04 职场文书
Anaconda配置各版本Pytorch的实现
2021/08/07 Python
详解MySql中InnoDB存储引擎中的各种锁
2022/02/12 MySQL