天天看點

vue頭像上傳與檔案壓縮

工作中遇到的問題記錄:vue開發頭像上傳元件,後端提供接口,需求為可相冊上傳,可相機拍攝上傳,檔案大小限制為2M

需求點分析

  1. 移動端調用相冊/攝像頭實作拍照
  2. 圖檔壓縮,目前高像素的相機拍出來的圖檔都有個4-5m大小,超出了限定的2m
  3. 圖檔上傳

第一點:移動端調用相冊/攝像頭實作拍照

這就比較簡單了,H5的标簽足已經能滿足我們的要求了,我們要做的隻是讓它更美觀一點

<div class="yb-headSet-but">
          <div class="btn-box">
            <input type="file" accept="image/*" @change="uploadImg" class="fromAlbum">   //設定input opacity為0,定位覆寫a标簽,a标簽設定為設計稿的按鈕樣式
            <a href="" class="weui-but">從相冊選一張</a>
          </div>
          <div class="btn-box">
            <a href="" class="weui-but">拍一張照片</a>
            <input type="file" accept="image/*" @change="uploadImg" capture="camera" class="fromCamera">    //兩者差別:capture="camera" 可直接調用攝像頭
          </div>
        </div>         
           

第二點:圖檔壓縮

利用的是canvas的drawImage 和 toDataURL的api,具體代碼是複制的 https://www.jianshu.com/p/4587312d2f44

修改了幾個大佬筆誤的地方

imgResize(file, callback) {
      var _this = this;
      var fileReader = new FileReader();
      fileReader.onload = function() {
        var IMG = new Image();
        IMG.src = this.result;
        IMG.onload = function() {
          var w = this.naturalWidth,
            h = this.naturalHeight,
            resizeW = 0,
            resizeH = 0;
          // maxSize 是壓縮的設定,設定圖檔的最大寬度和最大高度,等比縮放,level是報錯的品質,數值越小品質越低
          var maxSize = {
            width: 500,
            height: 500,
            level: 0.6
          };
          if (w > maxSize.width || h > maxSize.height) {
            var multiple = Math.max(w / maxSize.width, h / maxSize.height);
            resizeW = w / multiple;
            resizeH = h / multiple;
          } else {
            // 如果圖檔尺寸小于最大限制,則不壓縮直接上傳
            return callback(file);
          }
          var canvas = document.createElement("canvas");
          var ctx = canvas.getContext("2d");
          if (window.navigator.userAgent.indexOf("iPhone") > 0) {
            canvas.width = resizeH;
            canvas.height = resizeW;
            ctx.rotate(90 * Math.PI / 180);
            ctx.drawImage(IMG, 0, -resizeH, resizeW, resizeH);
          } else {
            canvas.width = resizeW;
            canvas.height = resizeH;
            ctx.drawImage(IMG, 0, 0, resizeW, resizeH);
          }
          var base64 = canvas.toDataURL("image/" + _this.fileType, maxSize.level);
          _this.convertBlob(window.atob(base64.split(",")[1]), callback);
        };
      };
      fileReader.readAsDataURL(file);
    },
           

使用canvas.toDataURL獲得的是base64格式,上傳時需要轉換成Blob二進制格式,使用formData送出

//Blob對象生成
convertBlob(base64, callback) {
      var buffer = new ArrayBuffer(base64.length);
      var ubuffer = new Uint8Array(buffer);
      for (var i = 0; i < base64.length; i++) {
        ubuffer[i] = base64.charCodeAt(i);
      }
      var blob;
      try {
        blob = new Blob([buffer], { type: "image/" + this.fileType });
      } catch (e) {
        window.BlobBuilder =
          window.BlobBuilder ||
          window.WebKitBlobBuilder ||
          window.MozBlobBuilder ||
          window.MSBlobBuilder;
        if (e.name === "TypeError" && window.BlobBuilder) {
          var blobBuilder = new BlobBuilder();
          blobBuilder.append(buffer);
          blob = blobBuilder.getBlob("image/" + this.fileType);
        }
      }
      callback(blob);
    },
//送出函數
postImg(file) {
      let image = new FormData();
      image.append("file", file, this.random_string(12) + '.' + this.fileType);
      console.log(image);
      setHead(image).then(res => {
        this.$indicator.close();
        if (res.Success) {
          let newPath = res.photoPath +'?t=' + this.random_string(12);
          this.$store.commit("UPDATE_BASEINFO", { photoPath: newPath });
          this.picValue = newPath;
          this.$messagebox.alert(res.Message);
        } else {
          this.$messagebox.alert(res.Message);
        }
      });
    },
           

注意注意 這裡有個巨大的坑。。。。。

這個坑是這樣的,我使用壓縮過的Blob格式送出背景時,傳回提示檔案格式不正确,不屬于jpeg、jpg等,這我就納悶了,我上傳的formData裡面寫明了檔案格式了啊;

後來經過簡書大佬 一斤代碼 老大的幫助,老大測試後說出了問題所在:那就是 FormData.append('file', file),如果隻有兩個參數,(第三個參數是檔案名)的情況下,預設fileName=“blob”,這樣上次至背景,如果背景未做其他處理的話,就會出現報錯的情況,後來就添加了随機檔案名,完整代碼如下

<template>
  <div class="yb-page">
    <div class="yb-page-inner">
      <div class="yb-headSetting">
        <div class="yb-headSet-img">
          <img :src="picValue" alt="" onerror="this.src='https://ixxxxxx.com/we/tmpls/t9000/img/account/[email protected]'">
        </div>
        <div class="yb-headSet-but">
          <div class="btn-box">
            <input type="file" ref="avatarInput" accept="image/*" @change="uploadImg" class="fromAlbum">
            <a href="" class="weui-but">從相冊選一張</a>
          </div>
          <div class="btn-box">
            <a href="" class="weui-but">拍一張照片</a>
            <input type="file" accept="image/*" @change="uploadImg" capture="camera" class="fromCamera">
          </div>
        </div>
      </div>
    </div>
  </div>
</template>
<script>
import { setHead } from "@/api/user";
export default {
  data() {
    return {
      picValue: this.$store.getters.baseInfo.photoPath,
      file: null,
      fileType: ''
    };
  },
  methods: {
    uploadImg(e) {
      if (e.target.value == "") {
        return;
      }
      this.$indicator.open();
      let files = e.target.files || e.dataTransfer.files;
      if (!files.length) return;
      this.file = files[0];
      this.fileType = this.file.type.split('/')[1];
      this.imgResize(files[0], this.postImg);
    },
    postImg(file) {
      let image = new FormData();
      image.append("file", file, this.random_string(12) + '.' + this.fileType);
      console.log(image);
      setHead(image).then(res => {
        this.$indicator.close();
        if (res.Success) {
          let newPath = res.photoPath +'?t=' + this.random_string(12);
          this.$store.commit("UPDATE_BASEINFO", { photoPath: newPath });
          this.picValue = newPath;
          this.$messagebox.alert(res.Message);
        } else {
          this.$messagebox.alert(res.Message);
        }
      });
    },
    imgResize(file, callback) {
      var _this = this;
      var fileReader = new FileReader();
      fileReader.onload = function() {
        var IMG = new Image();
        IMG.src = this.result;
        IMG.onload = function() {
          var w = this.naturalWidth,
            h = this.naturalHeight,
            resizeW = 0,
            resizeH = 0;
          // maxSize 是壓縮的設定,設定圖檔的最大寬度和最大高度,等比縮放,level是報錯的品質,數值越小品質越低
          var maxSize = {
            width: 500,
            height: 500,
            level: 0.6
          };
          if (w > maxSize.width || h > maxSize.height) {
            var multiple = Math.max(w / maxSize.width, h / maxSize.height);
            resizeW = w / multiple;
            resizeH = h / multiple;
          } else {
            // 如果圖檔尺寸小于最大限制,則不壓縮直接上傳
            return callback(file);
          }
          var canvas = document.createElement("canvas");
          var ctx = canvas.getContext("2d");
          if (window.navigator.userAgent.indexOf("iPhone") > 0) {
            canvas.width = resizeH;
            canvas.height = resizeW;
            ctx.rotate(90 * Math.PI / 180);
            ctx.drawImage(IMG, 0, -resizeH, resizeW, resizeH);
          } else {
            canvas.width = resizeW;
            canvas.height = resizeH;
            ctx.drawImage(IMG, 0, 0, resizeW, resizeH);
          }
          var base64 = canvas.toDataURL("image/" + _this.fileType, maxSize.level);
          _this.convertBlob(window.atob(base64.split(",")[1]), callback);
        };
      };
      fileReader.readAsDataURL(file);
    },
    convertBlob(base64, callback) {
      var buffer = new ArrayBuffer(base64.length);
      var ubuffer = new Uint8Array(buffer);
      for (var i = 0; i < base64.length; i++) {
        ubuffer[i] = base64.charCodeAt(i);
      }
      var blob;
      try {
        blob = new Blob([buffer], { type: "image/" + this.fileType });
      } catch (e) {
        window.BlobBuilder =
          window.BlobBuilder ||
          window.WebKitBlobBuilder ||
          window.MozBlobBuilder ||
          window.MSBlobBuilder;
        if (e.name === "TypeError" && window.BlobBuilder) {
          var blobBuilder = new BlobBuilder();
          blobBuilder.append(buffer);
          blob = blobBuilder.getBlob("image/" + this.fileType);
        }
      }
      callback(blob);
    },
    random_string(len) {
      len = len || 32;
      var chars = "ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678";
      var maxPos = chars.length;
      var pwd = "";
      for (var i = 0; i < len; i++) {
        pwd += chars.charAt(Math.floor(Math.random() * maxPos));
      }
      return pwd;
    }
  }
};
</script>