前端上传压缩图片代码

29 篇文章 0 订阅
3 篇文章 0 订阅
<!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>Document</title>
  </head>
  <body>
    <input id="fileInput" type="file" />
    <img id="img" src="" alt="" />
    <img id="blobImg" src="" alt="" />
    <img style="width: 90%;" id="yuanblobImg" src="" alt="" />
  </body>
  <script>
    let fileId = document.getElementById("fileInput");
    let img = document.getElementById("img");
    fileId.onchange = function (e) {
      console.log(e);
      let file = e.target.files[0]; //file文件
      fileToBase64(file, 0.2);
    };

    // 将file文件通过FileReader转化为base64格式
    //FileReader 对象允许Web应用程序异步读取存储在用户计算机上的文件(或原始数据缓冲区)的内容,使用File或 Blob对象指定要读取的文件或数据。

    const fileToBase64 = (file, quality) => {
      console.log(file.size / 1024 / 1024);
      //图片对象      图片质量
      console.log("将file文件通过FileReader转化为base64格式");
      let fileReader = new FileReader();
      let type = file.type; //"image/png"
      // 对文件进行读取
      fileReader.readAsDataURL(file);
      fileReader.onload = function (e) {
        console.log("原始二进制字符串:", this.result.toString());
        compress(fileReader.result, quality, type);
      };
    };

    //   处理base64数据,通过canvas(toDataURL)进行压缩绘制,然后输出压缩后的base64图片数据
    // 图片最大宽度
    const MAX_WIDTH = 800;
    const compress = (base64, quality, mimeType) => {
      //mimeType 图片类型,例如 mimeType='image/png'
      console.log(
        "处理base64数据,通过canvas(toDataURL)进行压缩绘制,然后输出压缩后的base64图片数据"
      );
      document.getElementById("yuanblobImg").src = base64;
      let cvs = document.createElement("canvas");
      let img = document.createElement("img");
      // CORS 策略,会存在跨域问题
      img.crossOrigin = "anonymous";
      return new Promise((resolve, reject) => {
        img.src = base64;
        // 图片偏移值
        let offetX = 0;
        img.onload = () => {
          if (img.width > MAX_WIDTH) {
            // 做适配
            cvs.width = MAX_WIDTH;
            cvs.height = (img.height * MAX_WIDTH) / img.width;
            offetX = (img.width - MAX_WIDTH) / 2;
          } else {
            cvs.width = img.width;
            cvs.height = img.height;
          }
          // 重点! 将图片插入画布并开始绘制
          let ctx = cvs
            .getContext("2d")
            .drawImage(img, 0, 0, cvs.width, cvs.height);

          let imageData = cvs.toDataURL(mimeType, quality);
          console.log("canvas图片压缩", imageData);
          // 我想转成blob格式看看
          let blobData = converrVase64UrlToBlob(imageData, mimeType);
          console.log("我想转成blob格式看看", blobData);
          console.log(blobData.size);
          console.log(2.8 * 1024 * 1024);
          //利用FormData传输数据
          // let formData = new window.FormData();
          // formData.append("file", _blob);
          resolve(imageData);
        };
      });
    };

    // base64数据转成blob文件流
    const converrVase64UrlToBlob = (base64, mimeType) => {
      //mimeType 图片类型,例如 mimeType='image/png'
      console.log("base64数据转成blob文件流");
      let bytes = window.atob(base64.split(",")[1]); //atob方法用于解码base64
      // 创建一个长度为 bytes.length 的 buffer(一个二进制文件), 它会分配一个 16 字节(byte)的连续内存空间,并用 0 进行预填充。
      let ab = new ArrayBuffer(bytes.length);

      // Uint8Array —— 将 ArrayBuffer 中的每个字节视为 0 到 255 之间的单个数字(每个字节是 8 位)。这称为 “8 位无符号整数”。
      let ia = new Uint8Array(ab);
      for (let i = 0; i < bytes.length; i++) {
        // 更改里面的初始化内容
        ia[i] = bytes.charCodeAt(i);
      }
      // 创建blob格式数据,并传入二进制文件和文件原本类型
      let _blob = new Blob([ia], { type: mimeType });
      toImg(_blob);
      return _blob;
    };

    // 尝试操作成功转化的blob文件
    // 利用createObjectURL转化为DataUrl格式
    const toImg = function (blobObj) {
      console.log("尝试操作成功转化的blob文件");
      // 两种方法都可以将Blob数据转化为DataUrl格式
      let imgSrc = window.URL.createObjectURL(blobObj);
      //let imgSrc = window.webkitURL.createObjectURL(blobObj);
      document.getElementById("blobImg").src = imgSrc;
    };
  </script>
</html>


 
// 将File(Blob)对象转变为一个dataURL字符串, 即base64格式
const fileToDataURL = file => new Promise((resolve) => {
  const reader = new FileReader();
  reader.onloadend = e => resolve(e.target.result);
  reader.readAsDataURL(file);
});
 
// 将dataURL字符串转变为image对象,即base64转img对象
const dataURLToImage = dataURL => new Promise((resolve) => {
  const img = new Image();
  img.onload = () => resolve(img);
  img.src = dataURL;
});
 
// 将一个canvas对象转变为一个File(Blob)对象
const canvastoFile = (canvas, type, quality) => new Promise(resolve => canvas.toBlob(blob => resolve(blob), type, quality));
 
const compress = (originfile, maxSize) => new Promise(async (resolve, reject) => {
  const originSize = originfile.size / 1024; // 单位为kb
  console.log('图片指定最大尺寸为', maxSize, '原始尺寸为:', originSize);
 
  // 将原图片转换成base64
  const base64 = await fileToDataURL(originfile);
 
  // 缩放图片需要的canvas
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
 
  // 小于maxSize,则不需要压缩,直接返回
  if (originSize < maxSize) {
    resolve({ compressBase64: base64, compressFile: originfile });
    console.log(`图片小于指定大小:${maxSize}KB,不用压缩`);
    return;
  }
 
 
  const img = await dataURLToImage(base64);
 
  const scale = 1;
  const originWidth = img.width;
  const originHeight = img.height;
  const targetWidth = originWidth * scale;
  const targetHeight = originHeight * scale;
 
  canvas.width = targetWidth;
  canvas.height = targetHeight;
  context.clearRect(0, 0, targetWidth, targetHeight);
  context.drawImage(img, 0, 0, targetWidth, targetHeight);
 
  // 将Canvas对象转变为dataURL字符串,即压缩后图片的base64格式
  // const compressedBase64 = canvas.toDataURL('image/jpeg', 0.1);
  // 经过我的对比,通过scale控制图片的拉伸来压缩图片,能够压缩jpg,png等格式的图片
  // 通过canvastoFile方法传递quality来压缩图片,只能压缩jpeg类型的图片,png等格式不支持
  // scale的压缩效果没有canvastoFile好
  // 在压缩到指定大小时,通过scale压缩的图片比通过quality压缩的图片模糊的多
  // 压缩的思路,用二分法找最佳的压缩点
  // 这里为了规避浮点数计算的弊端,将quality转为整数再计算;
  // const preQuality = 100;
  const maxQualitySize = { quality: 100, size: Number.MAX_SAFE_INTEGER };
  const minQualitySize = { quality: 0, size: 0 };
  let quality = 100;
  let count = 0; // 压缩次数
  let compressFinish = false; // 压缩完成
  let invalidDesc = '';
  let compressBlob = null;
 
  // 二分法最多尝试8次即可覆盖全部可能
  while (!compressFinish && count < 12) {
    compressBlob = await canvastoFile(canvas, 'image/jpeg', quality / 100);
    const compressSize = compressBlob.size / 1024;
    count++;
    if (compressSize === maxSize) {
      console.log(`压缩完成,总共压缩了${count}`);
      compressFinish = true;
      return;
    }
    if (compressSize > maxSize) {
      maxQualitySize.quality = quality;
      maxQualitySize.size = compressSize;
    }
    if (compressSize < maxSize) {
      minQualitySize.quality = quality;
      minQualitySize.size = compressSize;
    }
    console.log(`${count}次压缩,压缩后大小${compressSize},quality参数:${quality}`);
 
    quality = Math.ceil((maxQualitySize.quality + minQualitySize.quality) / 2);
 
    if (maxQualitySize.quality - minQualitySize.quality < 2) {
      if (!minQualitySize.size && quality) {
        quality = minQualitySize.quality;
      } else if (!minQualitySize.size && !quality) {
        compressFinish = true;
        invalidDesc = '压缩失败,无法压缩到指定大小';
        console.log(`压缩完成,总共压缩了${count}`);
      } else if (minQualitySize.size > maxSize) {
        compressFinish = true;
        invalidDesc = '压缩失败,无法压缩到指定大小';
        console.log(`压缩完成,总共压缩了${count}`);
      } else {
        console.log(`压缩完成,总共压缩了${count}`);
        compressFinish = true;
        quality = minQualitySize.quality;
      }
    }
  }
 
  if (invalidDesc) {
    // 压缩失败,则返回原始图片的信息
    console.log(`压缩失败,无法压缩到指定大小:${maxSize}KB`);
    reject({ msg: invalidDesc, compressBase64: base64, compressFile: originfile });
    return;
  }
 
  compressBlob = await canvastoFile(canvas, 'image/jpeg', quality / 100);
  const compressSize = compressBlob.size / 1024;
  console.log(`最后一次压缩(即第${count + 1}次),quality为:${quality},大小:${compressSize}`);
  const compressedBase64 = await fileToDataURL(compressBlob);
 
  const compressedFile = new File([compressBlob], originfile.name, { type: 'image/jpeg' });
 
  resolve({ compressFile: compressedFile, compressBase64: compressedBase64 });
});
 
 
export default compress;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值