Сжатие изображений Vue и загрузка на сервер

Vue.js

В этой статье в основном объясняется, как выбирать изображения на мобильной стороне на основе Vue + Vant, сжимать изображения с помощью Canvas и, наконец, загружать их на сервер. Он также будет инкапсулировать класс инструмента для прямого вызова.

1. Пакет инструментов

Без лишних слов, давайте начнем с кода и инкапсулируем его.CompressImageUtils Инструменты:

/**
 * 图片压缩工具类
 * 最大高度和最大宽度都为 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.

Здесь максимальная высота и максимальная ширина сжатия прямо прописаны как 500, и при вызове они не передаются. Поскольку логика сжатия и размер проекта обычно одинаковы, нет необходимости передавать его при каждом вызове. Конечно, если вы хотите писать более гибко, вы можетеcompressImageв методеmaxWidth,maxHeightи загрузка качества сжатия.

compressImageМетод возвращает значение большого двоичного объекта. Его можно изменить для возврата base64 в соответствии с потребностями интерфейса сервера.resolve(blob)изменить наresolve(base64)Вот и все.

Обратите внимание, что для некоторых изображений с шириной и высотой менее 500 и небольшим разрешением они могут быть больше, чем раньше, после сжатия. Можно предположить, что разрешение изображения, сгенерированного холстом, выше, чем у оригинала, поэтому итоговое изображение больше, чем до сжатия. Вы можете добавить суждение в вызывающем месте.Если сжатый размер меньше исходного изображения, загрузите сжатое изображение, если сжатый размер больше исходного изображения, загрузите исходное изображение.

2. Как использовать

БудуCompressImageUtilsВведите в целевой файл, а затем вызовитеcompressImageметод, вы можете получить сжатый результат в обратном вызове. Уведомление compressImageМетод возвращает обещание.
Опустите другой нерелевантный код и оставьте только тот, который связан со сжатием изображений и загрузкой:

<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,8 МБ и посмотрите на выходной лог.Сжатый размер составляет всего около 260 КБ. В это время оценивается, что сжатое изображение меньше, чем до сжатия, и сжатое изображение загружается на сервер.

image.png

Давайте посмотрим на маленькое изображение размером 300 × 300 и размером 12k Размер до сжатия 11252, а размер после сжатия 93656, что намного больше. В это время оценивается, что сжатое изображение больше, чем до сжатия, и загружается исходное изображение.

image.png

Резюме: этот класс инструментов имеет очевидные эффекты сжатия больших изображений.Независимо от того, насколько велико изображение, его размер после сжатия не превысит 300 КБ. Однако для некоторых небольших изображений он может быть больше после сжатия. Добавление сравнения размера после сжатия и до сжатия в месте вызова отлично решит эту проблему.
Конечно, об этом можно судить и внутри класса инструмента, но лично я считаю, что код, относящийся к бизнес-логике, лучше не выносить в публичный класс инструмента.