对用户上传的图片的尺寸进行等比缩放,size大小进行压缩

211 阅读1分钟

图片压缩和缩放的操作

操作背景

背景1

当前手机相机的像素极高,随便拍个照片都是尺寸3000*4000 px的,大小都在5M以上。

背景2

而服务器在接收图片时,为减轻服务器压力,一般对大小和尺寸是有限制的,比如限制尺寸为650*650 , 大小不得超过5M .

背景3

智能手机应用普及率极高,且手机照片在手机app里上传文件的使用越来越普遍。


  • 这时就出现了矛盾。 用户想用手机上传照片,而服务器希望用户用照片。
  • 于是, 作为前端,就要闪亮登场了, 我们可以让用户上传大照片,经过前端对图片尺寸的缩放和大小的压缩,让服务器接收到的是符合要求的小照片。既满足了用户,又满足了服务器, 双赢!

知识结构

核心要点

  • 输入框inputonchange事件
  • FileReader文件阅读器
  • img构造器Image
  • canvas画图
  • 目标宽高的等比缩放处理

功能拓展

  • base64blob形式的转换
  • 上传到服务器或云端

案例操作

<!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>
  <style>
  </style>
</head>
<body>
  <label for="upload_img">
    <span>上传图片 + </span>
  </label>
  <input type="file" id="upload_img" name="upload_img" style="display: none;" accept="image/jpeg">
  <script>
    var upload_img = document.querySelector('#upload_img');
    upload_img.onchange = function() {
      var pf = this.files;
      console.log(pf[0]);
      // pf[0]是刚才上传的图片,包含以下字段:
      /*
      {
        lastModified: 1634548550629, // 最后修改日期的时间戳
        lastModifiedDate: Mon Oct 18 2021 17:15:50 GMT+0800 (中国标准时间), // 最后修改的标准时间
        name: "222大.jpg", // 图片名
        size: 4824418, // 图片大小 单位字节
        type: "image/jpeg", // 图片类型
        webkitRelativePath: "", // 官方不建议使用的属性
      } */

      // 这里可以对上传的图片做一些基本处理
      // 比如检查图片是否上传了
      if(pf.length == 0) {
        console.log('图片没上传呢');
        return;
      }

      // 检查图片大小,比如不准超过5M
      var MAX_IMG_SIZE = 5 * 1024 * 1024;
      if(pf[0].size > MAX_IMG_SIZE) {
        console.log('上传失败,图片大小不能超过5M!')
        return;
      }

      // FileReader文件阅读器
      var reader = new FileReader();
      console.log('reader: ', reader);
      // reader包含很多事件方法和值  比如
      /*
      error 上传出错
      onabort 读取操作中断时触发
      onerror 读取报错时触发
      onload  读取完成后触发(成功了)
      onloadend 读取结束时触发  成功和失败都会触发
      onprogress 读取Blob时触发
      readyState 0表示没有加载任何数据  1 数据正在加载  2 数据读取完成
      result 根据读取方法, 返回不同格式的文件
      */

      // 使用Image构造器,构造img的dom
      var img = new Image();
      // console.log('img: ', img);
      /*
      这里得到的img实例是一个img标签
      可以直接操作自己的属性  比如
      img.width
      img.height
      img.src
      */

      // 设置一些需要用到的变量
      var _duf; // canvas画布生成的base64格式的dataurl文件
      var MAX_WIDTH = 650; // 最大宽度 这里以650为例
      var MAX_HEIGHT = 650; // 最大高度
      var upload_width; // 上传的原始宽度
      var upload_height; // 上传的原始高度
      var TARGET_WIDTH; // 目标宽度 即画到canvas上的最终宽度
      var TARGET_HEIGHT; // 目标高度 即画到canvas上的最终高度
      var fileType = pf[0].type; // 文件类型 后面会用到
      var fileName = pf[0].name; // 文件名 

      // readAsDataURL是读取文件方式
      // 读取文件后 reader的result属性中会包含一个data:URL格式的
      // Base64字符串 以表示读取文件的内容
      reader.readAsDataURL(pf[0]);
      console.log('已读reader: ', reader);
      /*
      {
        result: "data:image/jpeg;base64,/9j/4X3QRXhpZgAATU0AKgAAA..."
      }
      */

      // reader读取文件的事件
      // FileReader是继承自e.target的 所以可以用e事件
      reader.onload = function(e) {
        // 把读取到的url给到img
        img.src = e.target.result;
      }

      // 图片加载事件
      img.onload = function() {
        // 获取图片原始宽高
        upload_width = this.width;
        upload_height = this.height;
        // console.log('图片原始尺寸: ', upload_width, upload_height);

        // 进行等比缩放的操作
        if(upload_width > MAX_WIDTH || upload_height > MAX_HEIGHT) {
          if(upload_width / upload_height > MAX_WIDTH / MAX_HEIGHT) { // 上传的图片宽度较大
            TARGET_WIDTH = MAX_WIDTH;
            TARGET_HEIGHT = Math.floor(MAX_WIDTH * upload_height / upload_width);
          } else { // 上传的图片 高度较大
            TARGET_HEIGHT = MAX_HEIGHT;
            TARGET_WIDTH = Math.floor(MAX_HEIGHT * upload_width/ upload_height);
          }
        } else {
          TARGET_WIDTH = upload_width;
          TARGET_HEIGHT = upload_height;
        }

        // TARGET_WIDTH TARGET_HEIGHT就是最终被压缩后的宽高
        // console.log('target w h: ', TARGET_WIDTH, TARGET_HEIGHT);

        // 开始用画布处理图片
        var canvas = document.createElement('canvas');
        var context = canvas.getContext('2d');
        canvas.width = TARGET_WIDTH;
        canvas.height = TARGET_HEIGHT;
        // 清除画布
        context.clearRect(0, 0, TARGET_WIDTH, TARGET_HEIGHT);
        // 把图片放到canvas上 注意这里画布大小 是最终图片的大小
        context.drawImage(img, 0, 0, TARGET_WIDTH, TARGET_HEIGHT);
        // toDataURL()返回的是一个包含图片展示的data:URL
        // ()里可以为图片设定类型。默认是image/png格式
        _duf = canvas.toDataURL(fileType);
        console.log(_duf);
        this.src = _duf;
        // 这时的图片已经被压缩了,链接可以用了。 
        // 后续是上传到云端的介绍

        // 转换成blob备用
        var pf_n = baseToBlob(_duf);
        // 调用云端接口
        // xxx... 
        // 主要作用是生成一个完整的图片链接,方便应用
      }

      // 需要把base64形式的图片转换成blob形式的文件
      // 固定写法
      function baseToBlob(_duf) {
        var base_arr = _duf.split(',');
        var bstr = atob(base_arr[1]);
        var n = bstr.length;
        var u8arr = new Uint8Array(n);
        while (n--) {
          u8arr[n] = bstr.charCodeAt(n);
        }
        return new Blob([u8arr], {type: fileType});
      }
    }
  </script>
</body>
</html>

参考文档

  • 文件阅读器 https://developer.mozilla.org/zh-CN/docs/Web/API/FileReader
  • Image构造器 https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLImageElement/Image
  • 画布处理 https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toDataURL
  • 图片处理1 https://blog.csdn.net/weixin_33755649/article/details/91377182?utm_source=app&app_version=4.15.0&code=app_1562916241&uLinkId=usr1mkqgl919blen
  • 图片处理2 https://blog.csdn.net/impossible1994727/article/details/89091940
  • 图片处理3 https://blog.csdn.net/qq_41786458/article/details/90263201