JS压缩图片

817 阅读4分钟

为什么要前端来压缩图片

  • 因为现在h5手机拍照功能强大,导致图片动辄5,6M,如果这样子直接上传的话,不做限制,会导致流量庞大,服务器请求过慢
  • 服务端的上传图片是有要求图片大小,而且直接传这么大图片,带宽它也受不了,所以前端进行压缩图片就成了一个必要的环节

压缩流程

  1. 通过input标签拿到需要的上传图片文件
  2. 将拿到的图片文件对象转成img标签
  3. 在canvas上压缩绘制成HTMLImageElement
  4. 将canvas绘制的图像转成blob文件
  5. 将blob文件转成file对象文件(根据后台接口选择需不需要)
  6. 将blob文件或者file对象文件上传到服务器

1.通过input标签拿到文件

这一步不需要多说,毕竟都很熟悉了吧。。。通过原生的input标签,设置type熟悉为file,然后再设置accept属性,来限制选择的文件类型,比如现在要拿到图片,就设置为image/*,accept具体属性配置点击这里,然后绑定onChange事件,来获取选择后的文件

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

点击选择文件,获取值,得到的是file对象

1631072821(1).jpg

2.将拿到的图片文件对象转成img对象

拿到图片文件后,先将其转成HTMLImageElement,也就是普通的img标签,具体要使用 FileReader构造函数。

先new出来一个img和fileReader的实例,通过fileReader的 readAsDataURL这个api,来读取图片文件,其返回值是一个编码后的base64的字符串,然后将这个字符串赋值给img的src属性上,这样就完成了图片文件到 HTMLImageElement的转化。

// 先new一个img和fileReader的实例
const img = new Image()
const reader = new FileReader()// 读取文件资源
reader.readAsDataURL(file) 
reader.onload = function(e){ 
 img.src = e.target.result
}

3.在canvas上压缩绘制成HTMLImageElement

拿到转化后的img元素后,先取出该元素的宽高度,这个宽高度就是实际图片文件的宽高度。

const {width: originWidth, height: originHeight} = img

然后定义一个最大限度的宽高度,如果超过这个限制宽高度,则进行等比例的缩放

// 最大尺寸限制
const maxWidth = 600,maxHeihgt = 600
// 需要压缩的目标尺寸
let targetWidth = originWidth, targetHeight = originHeight
// 等比例计算超过最大限制时缩放后的图片尺寸
if (originWidth > maxWidth || originHeight > maxHeight) {
  if (originWidth / originHeight > 1) {
   // 宽图片
   targetWidth = maxWidth
   targetHeight = Math.round(maxWidth * (originHeight / originWidth))
  } else {
   // 高图片
   targetHeight = maxHeight
   targetWidth = Math.round(maxHeight * (originWidth / originHeight))
  }
 }

计算好需要的尺寸后,创建一个canvas实例对象,设置canvas为压缩后的尺寸,并把img绘制在canvas上

// 创建画布
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
 
// 设置宽高度为等同于要压缩图片的尺寸
 canvas.width = targetWidth
 canvas.height = targetHeight
 context.clearRect(0, 0, targetWidth, targetHeight)
 //将img绘制到画布上
 context.drawImage(img, 0, 0, targetWidth, targetHeight)

4.将canvas绘制的图像转成blob文件

canvas绘制完成后,就可以使用canvas内置方法toBlob(),将绘制的图像转成blob文件了

canvas.toBlob(function(blob) {
  resolve(blob)
}, type || 'image/png')
  • 第一个参数 callback 回调函数,可获得一个单独的Blob对象参数。
  • 第二个参数 type string类型,指定图片格式,默认格式为image/png可选

这里可以看到toBlob方法具体参数和使用方法

5.将blob文件转成file对象文件(根据后台接口选择需不需要)

将转成blob文件再转化成file文件

const newFile = new File([blob], 'test.png', {
     type: type || 'image/png'
 })
  • 第一个参数 bits 一个包含ArrayBufferArrayBufferViewBlob,或者 DOMString 对象的 Array — 或者任何这些对象的组合。这是 UTF-8 编码的文件内容
  • 第二个参数 name 表示文件名称,或者文件路径。

File对象实例的参数和用法

6.将blob文件或者file对象文件上传到服务器。

完整代码

    // 压缩前将file转换成img对象
    readImg(file) {
      return new Promise((resolve, reject) => {
        const img = new Image()
        const reader = new FileReader()
        reader.onload = function(e) {
          img.src = e.target.result
        }
        reader.onerror = function(e) {
          reject(e)
        }
        reader.readAsDataURL(file)
        img.onload = function() {
          resolve(img)
        }
        img.onerror = function(e) {
          reject(e)
        }
      })
    },
        
    /**
     * 压缩图片
     *@param img 被压缩的img对象
    * @param type 压缩后转换的文件类型
    * @param mx 触发压缩的图片最大宽度限制
    * @param mh 触发压缩的图片最大高度限制
    */
    compressImg(img, type, mx, mh) {
      return new Promise((resolve, reject) => {
        const canvas = document.createElement('canvas')
        const context = canvas.getContext('2d')
        const { width: originWidth, height: originHeight } = img
        // 最大尺寸限制
        const maxWidth = mx
        const maxHeight = mh
        // 目标尺寸
        let targetWidth = originWidth
        let targetHeight = originHeight
        if (originWidth > maxWidth || originHeight > maxHeight) {
          if (originWidth / originHeight > 1) {
            // 宽图片
            targetWidth = maxWidth
            targetHeight = Math.round(maxWidth * (originHeight / originWidth))
          } else {
            // 高图片
            targetHeight = maxHeight
            targetWidth = Math.round(maxHeight * (originWidth / originHeight))
          }
        }
        canvas.width = targetWidth
        canvas.height = targetHeight
        context.clearRect(0, 0, targetWidth, targetHeight)
        // 图片绘制
        context.drawImage(img, 0, 0, targetWidth, targetHeight)
        canvas.toBlob(function(blob) {
          resolve(blob)
        }, type || 'image/png')
      })
    },

由于过程中存在异步操作,所以需要用到promise或者async await

我这是使用的是async await

async upload(file) {
      const oFile = file.file
      const img = await this.readImg(oFile)
      const blob = await this.compressImg(img, oFile.type, 1024, 1024)
      const newFile = new File([blob], oFile['name'], {
        type: oFile.type
      })
      var formData = new FormData()
      formData.append('file', newFile)
      //  这是自己的api方法
      uploadFile(formData).then(res => {
        // 上传完之后的操作
      })
    },