溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

vue實(shí)現(xiàn)壓縮圖片預(yù)覽并上傳功能(promise封裝)

發(fā)布時(shí)間:2020-09-04 21:50:40 來(lái)源:腳本之家 閱讀:794 作者:zhaohanqq 欄目:web開發(fā)

本文實(shí)例為大家分享了vue實(shí)現(xiàn)壓縮圖片預(yù)覽并上傳的具體代碼,供大家參考,具體內(nèi)容如下

主要用到filereader、canvas 以及 formdata 這三個(gè)h6的api

過程大致分為三步:

用戶使用input file上傳圖片的時(shí)候,用filereader讀取用戶上傳的圖片數(shù)據(jù)(base64格式)
把圖片數(shù)據(jù)傳入img對(duì)象,然后將img繪制到canvas上,再調(diào)用canvas.toDataURL對(duì)圖片進(jìn)行壓縮
獲取到壓縮后的base64格式圖片數(shù)據(jù),轉(zhuǎn)成二進(jìn)制塞入formdata,再通過XmlHttpRequest提交formdata。

模板:

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

獲取圖片數(shù)據(jù)

methods: {
  //監(jiān)聽input file的change事件
 imageHandle(e) {
  //**這個(gè)是必不可少的,在下面的reader.onload中this就不再指vm了**
  let that = this;
  let maxSize = 100 * 1024;
  let files = e.srcElement.files;
  if (!files.length) return; //文件長(zhǎng)度大于0
  if (!/^image\//.test(files[0].type)) return; //必須是圖片才處理
  if (!window.FileReader) return; //支持FileReader
  //創(chuàng)建filereader對(duì)象
  let reader = new FileReader();
  reader.readAsDataURL(files[0]); //將圖片轉(zhuǎn)成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; //預(yù)覽圖片
   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);
   //圖片預(yù)覽
   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繪制圖片是有兩個(gè)限制的:

首先是圖片的大小,如果圖片的大小超過兩百萬(wàn)像素,圖片也是無(wú)法繪制到canvas上的,調(diào)用drawImage的時(shí)候不會(huì)報(bào)錯(cuò),但是你用toDataURL獲取圖片數(shù)據(jù)的時(shí)候獲取到的是空的圖片數(shù)據(jù)。

再者就是canvas的大小有限制,如果canvas的大小大于大概五百萬(wàn)像素(即寬高乘積)的時(shí)候,不僅圖片畫不出來(lái),其他什么東西也都是畫不出來(lái)的。

應(yīng)對(duì)第一種限制,處理辦法就是瓦片繪制了。瓦片繪制,也就是將圖片分割成多塊繪制到canvas上,我代碼里的做法是把圖片分割成100萬(wàn)像素一塊的大小,再繪制到canvas上。

而應(yīng)對(duì)第二種限制,我的處理辦法是對(duì)圖片的寬高進(jìn)行適當(dāng)壓縮,我代碼里為了保險(xiǎn)起見,設(shè)的上限是四百萬(wàn)像素,如果圖片大于四百萬(wàn)像素就壓縮到小于四百萬(wàn)像素。四百萬(wàn)像素的圖片應(yīng)該夠了,算起來(lái)寬高都有2000X2000了。

如此一來(lái)就解決了IOS上的兩種限制了。

除了上面所述的限制,還有兩個(gè)坑,一個(gè)就是canvas的toDataURL是只能壓縮jpg的,當(dāng)用戶上傳的圖片是png的話,就需要轉(zhuǎn)成jpg,也就是統(tǒng)一用canvas.toDataURL(‘image/jpeg', 0.1) , 類型統(tǒng)一設(shè)成jpeg,而壓縮比就自己控制了。

另一個(gè)就是如果是png轉(zhuǎn)jpg,繪制到canvas上的時(shí)候,canvas存在透明區(qū)域的話,當(dāng)轉(zhuǎn)成jpg的時(shí)候透明區(qū)域會(huì)變成黑色,因?yàn)閏anvas的透明像素默認(rèn)為rgba(0,0,0,0),所以轉(zhuǎn)成jpg就變成rgba(0,0,0,1)了,也就是透明背景會(huì)變成了黑色。解決辦法就是繪制之前在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;
  //如果圖片大于四百萬(wàn)像素,計(jì)算壓縮比并將大小壓至400萬(wàn)以下
  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萬(wàn)則使用瓦片繪制
  let count;
  if ((count = (width * height) / 1000000) > 1) {
  count = ~~(Math.sqrt(count) + 1); //計(jì)算要分成多少瓦片,~~在這里表示取整
  //計(jì)算每塊瓦片的寬高
  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);
  }
  //進(jìn)行壓縮
  let ndata = canvas.toDataURL("image/jpeg", 0.3);
  tCanvas.width = tCanvas.height = canvas.width = canvas.height = 0;
  return ndata;
 },

上傳

完成圖片壓縮后,就可以塞進(jìn)formdata里進(jìn)行上傳了,先將base64數(shù)據(jù)轉(zhuǎn)成字符串,再實(shí)例化一個(gè)ArrayBuffer,然后將字符串以8位整型的格式傳入ArrayBuffer,再通過BlobBuilder或者Blob對(duì)象,將8位整型的ArrayBuffer轉(zhuǎn)成二進(jìn)制對(duì)象blob,再將blob轉(zhuǎn)為File對(duì)象

_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 轉(zhuǎn)file
  var fileOfBlob = new File([blob], name, { type: type });
  return fileOfBlob;
 }
 }

將圖片壓縮上傳封裝到一個(gè)js文件里

const UploadImg = {
 imageHandle(files, maxSize, imgDom) {
  let that = this;
  let formdata = new FormData();
  let reader = new FileReader();
  reader.readAsDataURL(files[0]); //將圖片轉(zhuǎn)成base64格式
  //reader.onload是異步,要用到Promise對(duì)象將值返回出去
  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;
  //如果圖片大于四百萬(wàn)像素,計(jì)算壓縮比并將大小壓至400萬(wàn)以下
  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萬(wàn)則使用瓦片繪制
  let count;
  if ((count = (width * height) / 1000000) > 1) {
   count = ~~(Math.sqrt(count) + 1); //計(jì)算要分成多少瓦片
   //計(jì)算每塊瓦片的寬高
   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);
  }
  //進(jìn)行最小壓縮
  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 轉(zhuǎn)file
  var fileOfBlob = new File([blob], name, { type: type });
  return fileOfBlob;
 }
}

export default UploadImg

調(diào)用代碼

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; //文件長(zhǎng)度大于0
  if (!/^image\//.test(files[0].type)) return; //必須是圖片才處理
  if (!window.FileReader) return; //支持FileReader

  if (this.docEntry === "" || this.lineId === "") {
  this.$toast("請(qǐng)?zhí)顚懲暾畔?, "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"));
  });
 }
 }

參考鏈接:移動(dòng)端利用H5實(shí)現(xiàn)壓縮圖片上傳功能

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI