前端使用canvas压缩图片

205 阅读1分钟

----- 我正在参加「掘金·启航计划」------

前言

前端同学在开发过程中,总会遇到类似的需求——上传一个图片,并压缩到2M,接下来就讲述一下如何使用canvas和递归算法处理这类需求。

html

 <input type="file" id="ipt" accept="image/*" />

判断文件大小

let limitSize  = 2 //限定大小 2M
const isLt = file.size / 1024 / 1024 < limitSize;
if(!isLt){
//压缩
}

使用canvas绘制图片

  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  canvas.width = w;
  canvas.height = h;
  ctx.drawImage(img, 0, 0, w, h);

toDataURL压缩图片质量,生成base64图片

const base64 = canvas.toDataURL('image/jpeg', quality || 0.9);

将base64的图片转化成文件

function dataURLtoFile(dataurl, filename) {
      const arr = dataurl.split(',');
      const mime = arr[0].match(/:(.*?);/);
      const bstr = atob(arr[1]);
      let n = bstr.length;
      const u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], filename, { type: mime });
  }

递归处理,使文件大小达到2M以下

如果得到的图片大小仍然大于2M,则继续压缩,直到图片大小<2M,返回图片文件。

存储图片

最终得到图片的file文件

全部代码

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>文件压缩</title>
  </head>
  <body>
    <input type="file" id="ipt" accept="image/*" />
  </body>
  <script>
    let limitSize = 2;
    let ipt = document.querySelector('#ipt');
    ipt.addEventListener('change', (e) => {
      inputChange(e);
    });

    function inputChange(e) {
      let file = e.target.files[0];
      const isLt = file.size / 1024 / 1024 < limitSize;
      if (!isLt) {
        const filereader = new FileReader();
        filereader.readAsDataURL(file);
        filereader.onload = (res) => {
          const src = res.target.result;
          const image = new Image();
          image.src = src;
          image.onload = function (img) {
            // 获取图片的宽高,并存放到file对象中
            file.width = image.width * 0.8;
            file.height = image.height * 0.8;
            //   alert('您的图片太大,压缩中...');
            const newFile = compressImg(this, file.name, image.width * 0.8, image.height * 0.8, 0.9);
            // 存储文件
            // saveFilesFn(newFile);
          };
        };
      }
    }
    // 压缩图片
    function compressImg(img, name, w, h, quality) {
      // 生成canvas
      const canvas = document.createElement('canvas');
      const ctx = canvas.getContext('2d');
      canvas.width = w;
      canvas.height = h;
      ctx.drawImage(img, 0, 0, w, h);
      const base64 = canvas.toDataURL('image/jpeg', quality || 0.9);
      const file = dataURLtoFile(base64, name);
      const isLt = file.size / 1024 / 1024 < limitSize;

      if (!isLt) {
        //    超过2M 重新压缩
        return compressImg(img, name, w, h, 0.9);
      }
      //   低于2M 返回数据
      return dataURLtoFile(base64, name);
    }

    // 将base64的图片转换成文件
    function dataURLtoFile(dataurl, filename) {
      const arr = dataurl.split(',');
      const mime = arr[0].match(/:(.*?);/);
      const bstr = atob(arr[1]);
      let n = bstr.length;
      const u8arr = new Uint8Array(n);
      while (n--) {
        u8arr[n] = bstr.charCodeAt(n);
      }
      return new File([u8arr], filename, { type: mime });
    }
  </script>
</html>

总结

这个方法在移动端H5中作用很大,用户拍照时,图片质量往往大于2M, 使用此方法主动压缩图片质量,满足需求,减轻服务器压力。