Vue 圖片壓縮並上傳至服務器

本文主要講解基於 Vue + Vant ,實現移動端圖片選擇,並用 Canvas 壓縮圖片,最後上傳至服務器。還會封裝一個工具類,方便直接調用。javascript

1、工具類封裝

廢話很少說先上代碼,封裝一個 CompressImageUtils 工具類:vue

/** * 圖片壓縮工具類 * 最大高度和最大寬度都爲 500,若是超出大小將等比例縮放。 * * 注意可能出現壓縮後比原圖更大的狀況,在調用的地方本身判斷大小並決定上傳壓縮前或壓縮後的圖到服務器。 */

// 將base64轉換爲blob
export function convertBase64UrlToBlob(urlData) {
  let arr = urlData.split(',')
  let mime = arr[0].match(/:(.*?);/)[1]
  let bstr = atob(arr[1])
  let n = bstr.length
  let u8arr = new Uint8Array(n)
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n)
  }
  return new Blob([u8arr], {type: mime})
}


// 壓縮圖片
export function compressImage(path) {

  //最大高度
  const maxHeight = 500;
  //最大寬度
  const maxWidth = 500;

  return new Promise((resolve, reject) => {
    let img = new Image();
    img.src = path;
    img.onload = function () {
      const originHeight = img.height;
      const originWidth = img.width;
      let compressedWidth = img.height;
      let compressedHeight = img.width;
      if ((originWidth > maxWidth) && (originHeight > maxHeight)) {
        // 更寬更高,
        if ((originHeight / originWidth) > (maxHeight / maxWidth)) {
          // 更加嚴重的高窄型,肯定最大高,壓縮寬度
          compressedHeight = maxHeight
          compressedWidth = maxHeight * (originWidth / originHeight)
        } else {
          //更加嚴重的矮寬型, 肯定最大寬,壓縮高度
          compressedWidth = maxWidth
          compressedHeight = maxWidth * (originHeight / originWidth)
        }
      } else if (originWidth > maxWidth && originHeight <= maxHeight) {
        // 更寬,但比較矮,以maxWidth做爲基準
        compressedWidth = maxWidth
        compressedHeight = maxWidth * (originHeight / originWidth)
      } else if (originWidth <= maxWidth && originHeight > maxHeight) {
        // 比較窄,但很高,取maxHight爲基準
        compressedHeight = maxHeight
        compressedWidth = maxHeight * (originWidth / originHeight)
      } else {
        // 符合寬高限制,不作壓縮
      }
      // 生成canvas
      let canvas = document.createElement('canvas');
      let context = canvas.getContext('2d');
      canvas.height = compressedHeight;
      canvas.width = compressedWidth;
      context.clearRect(0, 0, compressedWidth, compressedHeight);
      context.drawImage(img, 0, 0, compressedWidth, compressedHeight);
      let base64 = canvas.toDataURL('image/*', 0.8);
      let blob = convertBase64UrlToBlob(base64);
      // 回調函數返回blob的值。也可根據本身的需求返回base64的值
      resolve(blob)
    }
  })
}
複製代碼

定義的最大寬度和最大高度均爲 500,若是圖片的寬高至少有一個超出了 500,都會被 **等比例 **壓縮,不用擔憂變形。能夠根據本身項目須要改變maxWidth 和 maxHeight 。java

這裏直接把壓縮的最大高度和最大寬度寫死爲 500 了,沒有在調用時傳。由於一個項目壓縮的邏輯和大小通常都一致的,不必在每次調用的時候傳。固然若是想寫的靈活一點,能夠在 compressImage 方法裏再把 maxWidth 、 maxHeight 和壓縮質量傳上。ios

compressImage 方法返回的是 blob 值,根據服務端接口須要能夠改成返回 base64,只需將 resolve(blob) 改成 resolve(base64) 便可。canvas

注意一點,對於有些寬高沒到 500,且分辨率很小的圖片,壓縮以後可能比以前還大。猜想多是 canvas 生成的圖片分辨率要比原來高一些,因此最終的圖片比壓縮前更大。能夠在調用的地方加個判斷,若是壓縮完的大小比原圖小,就上傳壓縮後的圖片;若是若是壓縮完的大小比原圖大,就上傳原圖。axios

2、如何使用

CompressImageUtils 引入到目標文件,而後調用 compressImage 方法,便可在回調裏得到壓縮後的結果。注意 compressImage 方法返回的是 Promise。
省略其餘無關代碼,只保留跟壓縮圖片和上傳相關的:api

<template>
  <div>
    <van-uploader v-model="fileList" :after-read="afterRead" />
  </div>
</template>

<script>
  import {compressImage} from '../../utils/CompressImageUtils'
  
  export default {
    components: {},
    methods: {
      
     //讀取完圖片後
      afterRead(file) {
        console.log('afterRead------', file);
        this._compressAndUploadFile(file);
      },

      //壓縮圖片上傳
      _compressAndUploadFile(file) {
        compressImage(file.content).then(result => {
          console.log('壓縮後的結果', result); // result即爲壓縮後的結果
          console.log('壓縮前大小', file.file.size);
          console.log('壓縮後大小', result.size);
          if (result.size > file.file.size){
            console.log('上傳原圖');
            //壓縮後比原來更大,則將原圖上傳
            this._uploadFile(file.file, file.file.name);
          } else {
            //壓縮後比原來小,上傳壓縮後的
            console.log('上傳壓縮圖');
            this._uploadFile(result, file.file.name)
          }
        })
      },

      //上傳圖片
      _uploadFile(file, filename) {
        let params = new FormData();
        params.append("file", file, filename);
        this.$api.uploadImage(params).then(res => {
          console.log('uploadImage', res);
					//上傳成功,寫本身的邏輯
        }).catch(err => {
          console.log('err', err);
        });
      }, 
    }
  }
</script>
複製代碼

在返回結果中加了層判斷,壓縮後比原來更大,則將原圖上傳;壓縮後比原來小,上傳壓縮後的。解決壓縮後比原圖更大的狀況。
this.$api.uploadImage(params) 是調用封裝的 api 方法,以下:服務器

//上傳圖片
 uploadImage(params){
    return axios.post(`${base}/api/v1/file`, params, {
      headers: {'content-type': 'multipart/form-data'}
    })
 },
複製代碼

3、使用效果

先上傳一個很是大的,尺寸爲 6016 × 4016,16.8M 的大圖,看輸出日誌,壓縮後大小僅爲 260k 左右。此時判斷壓縮後比壓縮前小,上傳壓縮圖到服務器。 app

image.png

再看個尺寸 300 × 300,12k 的小圖,壓縮前大小是 11252,壓縮後大小是 93656,大了不少。此時判斷壓縮後比壓縮前更大,上傳的是原圖。 函數

image.png

總結:這個工具類對大圖的壓縮效果很明顯,無論多大的圖,壓縮以後基本不會超過 300k。但對某些小圖可能出現壓縮完反而更大的狀況。在調用的地方加層壓縮後和壓縮前大小的比較判斷,會完美解決這個問題。 固然也能夠在工具類內部判斷,但我的以爲跟業務邏輯相關的代碼仍是不要放在公用的工具類比較好。

相關文章
相關標籤/搜索