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

本文实例为大家分享了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实现压缩图片上传功能

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

(0)

相关推荐

  • Vue.js 2.0 移动端拍照压缩图片预览及上传实例

    在学习和使用Vue.js 2.0 的过程中遇到不少不一样的地方,本来移动端开发H5应用,准备将mui框架和Vue.js+vue-router+vuex 全家桶结合起来使用,但是在拍照上传的实现过程中遇到了无法调用plus的H5+接口的问题,所以最后拍照上传功能还是使用input file方式里解决的.但是内心还是不甘心的,由于项目进度推进,迭代版本,所以不得不放弃,后续可能我将此功能使用调用H5+接口实现. 首先我来讲我实现这个拍照预览压缩上传的思路,准确的说应该是拍照或选择图片压缩之后预览及上

  • vue element upload实现图片本地预览

    vue使用element实现本地预览,最主要的是将图片路径转换为base64,供大家参考,具体内容如下 HTML <el-upload class="avatar-uploader" action="123" //这个路径不重要,可以随便写 :show-file-list="false" :on-success="handleAvatarSuccess" :on-change="onchange" :

  • Vue.js图片预览插件使用详解

    Vue.js 是什么 Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架.与其它大型框架不同的是,Vue 被设计为可以自底向上逐层应用.Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合.另一方面,当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动. 如果你想在深入学习 Vue 之前对它有更多了解,我们制作了一个视频,带您了解其核心概念和一个示例工程. 如果你已经是有经验的前端开发者,想知道 Vue

  • vue项目中实现图片预览的公用组件功能

    今天产品提出了一个查看影像的功能需求. 在查看单据的列表中,有一列是影像字段,一开始根据单据号调用接口查看是否有图片附件,如果有则弹出一个全屏的弹出层,如果没有给出提示.而且,从列表进入详情之后,附件那边也会有一个查看影像的按钮. 所以,根据需求,多个组件需要用到查看影像的功能,所以考虑做一个公用组件,通过组件传值的方法将查看影像文件的入参传过去. 后来,产品要求图片可以旋转缩放. 废话不多说,贴上代码: <template> <div class="filePreview&q

  • 基于Vue2x的图片预览插件的示例代码

    本文介绍了基于Vue2x的图片预览插件的示例代码,分享给大家,具体如下: 先来看下Demo LiveDemo 关于开发Vue插件的几种方式 (具体请移步官网)Vue官网 MyPlugin.install = function (Vue, options) { // 1. 添加全局方法或属性 Vue.myGlobalMethod = function () { // 逻辑... } // 2. 添加全局资源 Vue.directive('my-directive', { bind (el, bin

  • vue实现图片预览组件封装与使用

    这是移动端使用vue框架与mint-ui实现的父用子之间的通信实现的图片预览的功能,在父组件中每一张图片都可以实现图片放大查看. 子组件 <!--html部分--> <template> <div id="imgEnlarge" ref="imgEnlarge" class="img-bg" @click="imgBgHide" v-show="isShow"> <

  • vue2.0 使用element-ui里的upload组件实现图片预览效果方法

    1.首先我们在cli中引入element-ui 2.然后在具体的代码中放入uoload组件 <el-upload class="upload-demo" action="" :auto-upload='false' :on-change='changeUpload'> <el-button size="small" type="primary">点击上传</el-button> <di

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

    本文实例为大家分享了vue实现压缩图片预览并上传的具体代码,供大家参考,具体内容如下 主要用到filereader.canvas 以及 formdata 这三个h5的api 过程大致分为三步: 用户使用input file上传图片的时候,用filereader读取用户上传的图片数据(base64格式) 把图片数据传入img对象,然后将img绘制到canvas上,再调用canvas.toDataURL对图片进行压缩 获取到压缩后的base64格式图片数据,转成二进制塞入formdata,再通过Xm

  • AJAX实现图片预览与上传及生成缩略图的方法

    要实现功能,上传图片时可以预览,因还有别的文字,所以并不只上传图片,实现与别的文字一起保存,当然上来先上传图片,然后把路径和别的文字一起写入数据库:同时为 图片生成缩略图,现只写上传图片方法,文字在ajax里直接传参数就可以了,若要上传多图,修改一下就可以了. 借鉴了网上资料,自己写了一下,并不需要再新加页面,只在一个页面里就OK啦. JS代码: //ajax保存数据,后台方法里实现此方法 function SaveData() { filename = document.getElementB

  • vue+vant使用图片预览功能ImagePreview的问题解决

    如果您搜到这篇文章的话,那员外估计您遇到跟我一样的问题了,即在打开图片预览功能后,如果不关闭预览的图片,同时改变路由的话,会发现即使路由改变了,预览的图片还在文档的最顶层显示,如图: 着实让员外百思不其解,在调用vant 的 ImagePreview图片预览组件中,没有html,有的只是引入ImagePreview 和js的调用.在这种情况下,员外是想自己添加任何方法都没法实现的,同时想在离开路由时的beforeRouteLeave 钩子中关闭这个 ImagePreview  也实现不了,因为根

  • Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能详解

    本文实例讲述了Vue + Node.js + MongoDB图片上传组件实现图片预览和删除功能.分享给大家供大家参考,具体如下: 公司要写一些为自身业务量身定制的的组件,要基于Vue,写完后扩展了一下功能,选择写图片上传是因为自己之前一直对这个功能比较迷糊,所以这次好好了解了一下.演示在网址打开后的show.gif中. 使用技术:Vue.js | node.js | express | MongoDB. github网址:https://github.com/neroneroffy/privat

  • Vue实现图片预览效果实例(放大、缩小、拖拽)

    前言 这张图是显示的图片放大的一个预览情况,这里是参考预览操作实现的一个背景为黑色的部分,上层的图片可实现滚轮放大或者点击上部的放大镜图标进行放大,代码是基于Ant Design Vue框架的基础上 这里先分解部分,后面有全部代码 1.需要有黑色背景用于预览背景: 这里的背景要占满整个屏幕(这里的一般是参考其他插件预览的样式进行模拟设计的),样式在后方代码内 2.展示图片并且把图片展示到背景板最中间. 3.最重要的下方的两部分: 滚轮放大缩小图片: bbimg() { let e = e ||

  • input file上传 图片预览功能实例代码

    input file上传图片预览其实很简单,只是没做过的感觉很神奇,今天我就扒下她神秘的面纱,其实原理真的很简单,下面通过一段代码大家都明白了. 具体代码如下所示: <!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title></title> <script src="jquery.js"></script>

  • jquery.uploadView 实现图片预览上传功能

    图片上传,网上有好多版本,今天也要做一个查了好多最终找到了一个uploadview 进行了一下修改 来看代码 @{ Layout = null; } <!DOCTYPE html> <html> <head> <meta name="viewport" content="width=device-width" /> <title>Index</title> <script src=&quo

随机推荐