前端图片压缩

157 阅读2分钟

图片压缩流程

  • 使用input或上传组件读取到图片文件file ,使用FileReader将其转换为 base64 编码;
  • 创建Image对象,读取转化后的base64;
  • 创建canvas ,将新创建的图片画到canvas上;
  • 利用 canvas.toDataURL/toBlob 将canvas对象转换为 base64 或 Blob;
  • 最后将 base64 或 Blob 转化为 File返回;

toDataURL介绍

canvas的toDataURL()方法是返回一个包含图片展示的 数据URL。可以使用 type 参数其类型,默认为png格式。图片的分辨率为96dpi。

canvas.toDataURL(type, encoderOptions);
  • type指定转换为base64编码后图片的格式,如:image/pngimage/jpegimage/webp等等,默认为image/png格式;
  • encoderOptions用于设置转换为base64编码后图片的质量,取值范围为0-1,超出取值范围用默认值0.92代替;

返回值是一个数据url,是base64组成的图片的源数据、可以直接赋值给图片的src属性。

/**
 * 压缩图片方法
 * @param {file} file 文件或文件数组
 * @param {Number} quality 图片质量(取值0-1之间默认0.92)
 */
const compressImg = (file, quality) => {
  if (Array.isArray(file)) {
     // 处理图片数组
    return Promise.all(Array.from(file).map((e) => compressImg(e, quality)));
  } else {
    let qualitys = 1;
    //单位:MB
    let fileSize = parseInt((file.size / 1024 / 1024).toFixed(2));
    if (1 < fileSize && fileSize < 5) {
      qualitys = 0.92;
    }
    if (5 < fileSize && fileSize < 10) {
      qualitys = 0.85;
    }
    if (10 < fileSize) {
      qualitys = 0.52;
    }
    if (quality) {
      qualitys = quality;
    }
    return new Promise((resolve) => {
      // 压缩质量大于等于1不处理
      if (qualitys >= 1) {
        resolve(file);
      } else {
        // 读取原来的文件类型,不是图片类型不处理
        let fileType = file.type;
        if (!fileType || !fileType.includes("image/")) {
          resolve(file);
          return;
        }
        const fileReader = new FileReader(); // 创建 FileReader
        fileReader.onload = ({ target: { result: src } }) => {
          const image = new Image(); // 创建 img 元素
          image.onload = () => {
            // 创建 canvas 元素
            const mCanvas = document.createElement("canvas"); 
            const mCtx = mCanvas.getContext("2d");
            let targetWidth = image.width;
            let targetHeight = image.height;
            let originWidth = image.width;
            let originHeight = image.height;
            if (1 <= fileSize) {
              let maxWidth = 1400;
              let maxHeight = 1400;
              if (5 < fileSize) {
                maxWidth = 1920;
                maxHeight = 1920;
              }
              targetWidth = originWidth;
              targetHeight = originHeight;
              // 图片尺寸超过的限制
              if (originWidth > maxWidth || originHeight > maxHeight) {
                if (originWidth / originHeight > maxWidth / maxHeight) {
                  // 更宽,按照宽度限定尺寸
                  targetWidth = maxWidth;
                  targetHeight = Math.round(
                    maxWidth * (originHeight / originWidth)
                  );
                } else {
                  targetHeight = maxHeight;
                  targetWidth = Math.round(
                    maxHeight * (originWidth / originHeight)
                  );
                }
              }
            }
            mCanvas.width = targetWidth;
            mCanvas.height = targetHeight;
            mCtx.clearRect(0, 0, targetWidth, targetHeight);
             // 将图片画到canvas上
            mCtx.drawImage(image, 0, 0, targetWidth, targetHeight);
            const canvasURL = mCanvas.toDataURL(fileType, qualitys);
            const buffer = atob(canvasURL.split(",")[1]);
            let length = buffer.length;
            const bufferArray = new Uint8Array(new ArrayBuffer(length));
            while (length--) {
              bufferArray[length] = buffer.charCodeAt(length);
            }
            const miniFile = new File([bufferArray], file.name, {
              type: fileType
            });
            resolve(miniFile);
          };
          image.src = src;
        };
        fileReader.readAsDataURL(file);
      }
    });
  }
};