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 相关文章推荐
js读写cookie实现一个底部广告浮层效果的两种方法
Dec 29 Javascript
Javascript添加监听与删除监听用法详解
Dec 19 Javascript
在for循环中length值是否需要缓存
Jul 27 Javascript
Javascript typeof与instanceof的区别
Oct 18 Javascript
学习使用Bootstrap输入框、导航、分页等常用组件
May 11 Javascript
jQuery插件select2利用ajax高效查询大数据列表(可搜索、可分页)
May 19 jQuery
深入理解vue Render函数
Jul 19 Javascript
JS实现评价的星星功能
Aug 20 Javascript
基于jQuery实现的单行公告活动轮播效果
Aug 23 jQuery
layui点击按钮添加可编辑的一行方法
Aug 15 Javascript
react 国际化的实现代码示例
Sep 14 Javascript
Vue动态加载异步组件的方法
Nov 21 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面向对象全攻略 (十四) php5接口技术
2009/09/30 PHP
php导出word文档与excel电子表格的简单示例代码
2014/03/08 PHP
php中cookie的使用方法
2014/03/29 PHP
Laravel 4 初级教程之Pages、表单验证
2014/10/30 PHP
浅谈php提交form表单
2015/07/01 PHP
Zend Framework基本页面布局分析
2016/03/19 PHP
php 三大特点:封装,继承,多态
2017/02/19 PHP
php将html转为图片的实现方法
2017/05/19 PHP
PHP实现类似于C语言的文件读取及解析功能
2017/09/01 PHP
在Laravel5.6中使用Swoole的协程数据库查询
2018/06/15 PHP
一个JS翻页效果
2007/07/23 Javascript
Javascript动态绑定事件的简单实现代码
2010/12/25 Javascript
Js 时间函数getYear()的使用问题探讨
2013/04/01 Javascript
javascript实用小函数使用介绍
2013/11/11 Javascript
JavaScript中原型和原型链详解
2015/02/11 Javascript
javascript入门之数组[新手必看]
2016/11/21 Javascript
使用Angular缓存父页面数据的方法
2017/01/03 Javascript
nodejs个人博客开发第五步 分配数据
2017/04/12 NodeJs
vuejs2.0子组件改变父组件的数据实例
2017/05/10 Javascript
全面解析vue router 基本使用(动态路由,嵌套路由)
2018/09/02 Javascript
Swiper实现导航栏滚动效果
2020/10/16 Javascript
[45:18]完美世界DOTA2联赛循环赛 PXG vs IO 第二场 11.06
2020/11/09 DOTA
小议Python中自定义函数的可变参数的使用及注意点
2016/06/21 Python
Python Flask基础教程示例代码
2018/02/07 Python
Python使用matplotlib 画矩形的三种方式分析
2019/10/31 Python
Python迭代器协议及for循环工作机制详解
2020/07/14 Python
Pycharm安装Qt Design快捷工具的详细教程
2020/11/18 Python
微软开源最强Python自动化神器Playwright(不用写一行代码)
2021/01/05 Python
css3实现3D文本悬停改变效果的示例代码
2019/01/16 HTML / CSS
付款委托书范本
2014/04/04 职场文书
大学生考试作弊检讨书1000字
2014/10/14 职场文书
2014年环卫工作总结
2014/11/22 职场文书
工厂门卫岗位职责
2015/04/13 职场文书
教师节主持词开场白
2015/05/29 职场文书
东京审判观后感
2015/06/01 职场文书
django上传文件的三种方式
2021/04/29 Python