4

一、介绍

本文将介绍再Vue项目中通过js对图片进行压缩后使用。(纯前端实现,不依赖后台)

主要使用Canvas.toDataURL(type, encoderOptions)对图片大小进行调整

参数描述
type图片格式
encoderOptions在指定图片格式为 image/jpeg 或 image/webp的情况下,可以从 0 到 1 的区间内选择图片的质量。

二、步骤

1.将图片转化为 base64

使用 FileReader.readAsDataURL(file):该方法会读取指定的 File 对象。读取操作完成的时候,readyState 会变成已完成(DONE),并触发 loadend 事件,同时 result 属性将包含一个data:URL格式的字符串(base64编码)以表示所读取文件的内容。

reader.readAsDataURL 把图片能转换成 base64, 出发 reader.onload 事件对 image.src 赋值完成,触发 image.onload 事件生成canvas,调用回调函数。

clipboard.png

/**
 * 将图片转化为base64
 */
imgBase64(file, callback) {
  // 看支持不支持FileReader
  if (!file || !window.FileReader) return;
  // 创建一个 Image 对象
  let image = new Image();
  // 绑定 load 事件处理器,加载完成后执行
  image.onload = function () {
    // 创建 canvas DOM 对象
    let canvas = document.createElement('canvas');
    // 返回一个用于在画布上绘图的环境, '2d' 指定了您想要在画布上绘制的类型
    let ctx = canvas.getContext('2d');
    // 如果高度超标 // 参数,最大高度
    let MAX_HEIGHT = 3000;
    if (image.height > MAX_HEIGHT) {
      // 宽度等比例缩放 *=
      image.width *= MAX_HEIGHT / image.height;
      image.height = MAX_HEIGHT;
    }
    // 获取 canvas的 2d 环境对象,
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    // 重置canvas宽高
    canvas.width = image.width;
    canvas.height = image.height;
    // 将图像绘制到canvas上
    ctx.drawImage(image, 0, 0, image.width, image.height);

    callback(image, canvas);

  };
  if (/^image/.test(file.type)) {
    // 创建一个reader
    let reader = new FileReader();
    // 读取成功后的回调
    reader.onload = function () {
      // 设置src属性,浏览器会自动加载。
      // 记住必须先绑定事件,才能设置src属性,否则会出同步问题。
      image.src = this.result;
    };
    // 将图片将转成 base64 格式
    reader.readAsDataURL(file);
  }
}

2.把base64转换成file文件

/**
 * 把base64转换成file文件
 */
convertBase64UrlToFile(dataurl, filename) {
  let arr = dataurl.split(','),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]), n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new File([u8arr], filename, {type: mime});
}

3.ES6 Promise的简单使用

function checkAndHandleCompression() {
  //当异步代码执行成功时,我们才会调用resolve(...), 当异步代码失败时就会调用reject(...)
  return new Promise((resolve, reject) => {
    resolve('成功!') // 代码正常执行!
  })
}

checkAndHandleCompression().then((data)=>{
  console.log(data) // print 成功!
});

4.对 Promise 填充代码,检查并压缩图片大小

如果图片过大就使用Canvas.toDataURL(type, encoderOptions)对图片进行压缩,并将结果异步返回
/**
 * 检查并压缩图片大小
 */
checkAndHandleCompression(file) {

  return new Promise((resolve, reject) => {

    this.imgBase64(file, (image, canvas) => {
      let maxSize = 2 * 1024; // 压缩到2M以下 (单位KB)
      let fileSize = file.size / 1024; // 图片大小 (单位KB)

      let uploadSrc, uploadFile;
      // 如果图片大小大于maxSize,进行压缩
      if (fileSize > maxSize) {
        uploadSrc = canvas.toDataURL(file.type, maxSize / fileSize);
        uploadFile = this.convertBase64UrlToFile(uploadSrc, file.name); // 转成file文件
      } else {
        uploadSrc = image.src;
        uploadFile = file;
      }

      let compressedSize = uploadFile.size / 1024;// 压缩后图片大小 (单位KB)
      // 判断图片大小是否小于maxSize,如果大于则继续压缩至小于为止
      if (compressedSize.toFixed(2) > maxSize) {
        this.checkAndHandleCompression(uploadFile);
      } else {
        let fileOptions = {uploadSrc, uploadFile};
        resolve(fileOptions);
      }
    });

  });

}

三、完整代码

创建mixins/image-compress.js

export default {

  methods: {
    /**
     * 检查并压缩图片大小
     */
    checkAndHandleCompression(file) {

      return new Promise((resolve, reject) => {

        this.imgBase64(file, (image, canvas) => {
          let maxSize = 2 * 1024; // 2M (单位KB)
          let fileSize = file.size / 1024; // 图片大小 (单位KB)

          let uploadSrc, uploadFile;
          // 如果图片大小大于maxSize,进行压缩
          if (fileSize > maxSize) { 
            uploadSrc = canvas.toDataURL(file.type, maxSize / fileSize); // 转换成DataURL
            uploadFile = this.convertBase64UrlToFile(uploadSrc, file.name); // 转成file文件
          } else {
            uploadSrc = image.src;
            uploadFile = file;
          }

          let compressedSize = uploadFile.size / 1024;// 压缩后图片大小 (单位KB)
          // 判断图片大小是否小于maxSize,如果大于则继续压缩至小于为止
          if (compressedSize.toFixed(2) > maxSize) {
            this.checkAndHandleUpload(uploadFile);
          } else {
            let fileOptions = {uploadSrc, uploadFile};
            resolve(fileOptions);
          }
        });

      });

    },

    /**
     * 将图片转化为base64
     */
    imgBase64(file, callback) {
      // 看支持不支持FileReader
      if (!file || !window.FileReader) return;
      // 创建一个 Image 对象
      let image = new Image();
      // 绑定 load 事件处理器,加载完成后执行
      image.onload = function () {
        // 创建 canvas DOM 对象
        let canvas = document.createElement('canvas');
        // 返回一个用于在画布上绘图的环境, '2d' 指定了您想要在画布上绘制的类型
        let ctx = canvas.getContext('2d');
        // 如果高度超标 // 参数,最大高度
        let MAX_HEIGHT = 3000;
        if (image.height > MAX_HEIGHT) {
          // 宽度等比例缩放 *=
          image.width *= MAX_HEIGHT / image.height;
          image.height = MAX_HEIGHT;
        }
        // 获取 canvas的 2d 环境对象,
        // 可以理解Context是管理员,canvas是房子
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        // 重置canvas宽高
        canvas.width = image.width;
        canvas.height = image.height;
        // 将图像绘制到canvas上
        ctx.drawImage(image, 0, 0, image.width, image.height);

        callback(image, canvas);

      };
      if (/^image/.test(file.type)) {
        // 创建一个reader
        let reader = new FileReader();
        // 读取成功后的回调
        reader.onload = function () {
          // 设置src属性,浏览器会自动加载。
          // 记住必须先绑定事件,才能设置src属性,否则会出同步问题。
          image.src = this.result;
        };
        // 将图片将转成 base64 格式
        reader.readAsDataURL(file);
      }
    },

    /**
     * 把Base64转换成file文件
     */
    convertBase64UrlToFile(dataurl, filename) {
      let arr = dataurl.split(','),
        mime = arr[0].match(/:(.*?);/)[1],
        bstr = atob(arr[1]), n = bstr.length,
        u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], filename, {type: mime});
    }

  }
};

四、example

<template>
    ...
    
    <input type="file" @change="handleUploadImage"/>
    
    ...
</template>
<script>

  import imageUploadMixins from '@/mixins/image-compress';
  
  export default {
  
    mixins: [imageUploadMixins],
    
    ...
    
    methods:{
        handleUploadImage(e){
            let file = e.target.files[0];
            this.checkAndHandleCompression(file).then( fileOptions => {
                // let {uploadSrc, uploadFile} = fileOptions;
                
                // 压缩完成使用 uploadSrc, uploadFile
                
                ...
                
            });
        }
    }
    
    ...
    
  }
</script>
<style>
    ...
</style>

五、参考链接


一念
112 声望3 粉丝