zoukankan      html  css  js  c++  java
  • 前端/JS/React/ES6:纯前端实现图片压缩技术

    网上图片压缩文章抄来抄去,真没意思,我自己写一个

    import React from 'react';
    // import logo from './logo.svg';
    import './App.css';
    
    function App() {
    
      const handleChange = e => {
        e.persist() // 为了看到原生file的内容,不加这句,原生file内容会被react隐藏
        let fileObj = e.target.files[0]
        // console.log(e)
        console.log(fileObj)
    
        // 设定标准上限为1MB,进行压缩处理
        compress(fileObj, 1024, (data) => {
          console.log(data)
        })
      }
    
      // 异步读取图片的promise
      const loadImageAsync = (url) => {
        return new Promise(function (resolve, reject) {
          const image = new Image()
    
          image.onload = function () {
            resolve(image)
          };
    
          image.onerror = function () {
            reject(new Error('Could not load image at ' + url))
          };
    
          image.src = url
        })
      }
    
      // 异步转换成base64编码的promise
      const fileToImgAsync = (file) => {
        return new Promise(function (resolve, reject) {
          const reader = new FileReader()
    
          reader.onload = function (e) {
            resolve(e.target.result);
          };
    
          reader.onerror = function () {
            reject(new Error('readAsDataURL:fail'))
          };
    
          reader.readAsDataURL(file)
        });
      }
    
      const downloadFileByBlob = (blobUrl, filename) => {
        const a = document.createElement('a')
        a.download = filename
        a.style.display = 'none'
        a.href = blobUrl
        // 触发点击
        document.body.appendChild(a)
        a.click()
        // 然后移除
        document.body.removeChild(a)
      }
    
      // async 搭配 promise 使用
      const compress = async (file, maxSizeKB, succFunc) => {
        if (file.size > 3 * 1024 * 1024) {
          let rate = 0 // 压缩率
    
          // 文件转图片
          const dataUrl = await fileToImgAsync(file)
    
          // 图片转画布
          const image = await loadImageAsync(dataUrl)
          // console.log(dataUrl, image)
    
          // 文件大小KB, file.size给的是字节Byte
          const fileSizeKB = file.size / 1024
          console.log(fileSizeKB)
    
          // 当图片大小超标,才进行压缩
          if (fileSizeKB > maxSizeKB) {
            // 计算压缩率
            rate = (fileSizeKB - maxSizeKB) / fileSizeKB
            console.log('压缩率:', rate)
            console.log('压缩后文件大小:', fileSizeKB * (1 - rate), 'kb')
          }
    
          // 画布执行压缩
          let canvas = document.createElement('canvas')
          let context = canvas.getContext('2d')
          const cvWidth = image.width * (1 - rate)
          const cvHeight = image.height * (1 - rate)
          console.log(image.width, image.height, cvWidth, cvHeight)
    
          canvas.height = cvHeight
          canvas.width = cvWidth
          context.clearRect(0, 0, cvWidth, cvHeight)
          context.drawImage(image, 0, 0, cvWidth, cvWidth)
    
          // 导出图片
          canvas.toBlob((blob) => {
    
            // 方式一:下载到本地
            // const blobUrl = window.URL.createObjectURL(blob)
            // downloadFileByBlob(blobUrl, file.name)
    
            // 方式二:生成网页可读取的对象
            const newImage = new File([blob], file.name, { type: file.type });
            succFunc(newImage)
          });
        }
      }
    
      return (
        <div className="App">
          <input type="file" id="file" onChange={handleChange} />
        </div>
      );
    }
    
    export default App;

    其实我们可以发现一个问题,导出的图片,在window系统中,大小是 207kb并不是1024kb,很明显size的比例用在width height的比例上有出入,要加入调节因子,因此算法还可以优化,如下

    import React from 'react';
    // import logo from './logo.svg';
    import './App.css';
    
    function App() {
    
      const handleChange = e => {
        e.persist() // 为了看到原生file的内容,不加这句,原生file内容会被react隐藏
        let fileObj = e.target.files[0]
        // console.log(e)
        console.log(fileObj)
    
        // 设定标准上限为1MB,进行压缩处理
        compress(fileObj, 1024, (data) => {
          console.log(data)
        })
      }
    
      // 异步读取图片的promise
      const loadImageAsync = (url) => {
        return new Promise(function (resolve, reject) {
          const image = new Image()
    
          image.onload = function () {
            resolve(image)
          };
    
          image.onerror = function () {
            reject(new Error('Could not load image at ' + url))
          };
    
          image.src = url
        })
      }
    
      // 异步转换成base64编码的promise
      const fileToImgAsync = (file) => {
        return new Promise(function (resolve, reject) {
          const reader = new FileReader()
    
          reader.onload = function (e) {
            resolve(e.target.result);
          };
    
          reader.onerror = function () {
            reject(new Error('readAsDataURL:fail'))
          };
    
          reader.readAsDataURL(file)
        });
      }
    
      const downloadFileByBlob = (blobUrl, filename) => {
        const a = document.createElement('a')
        a.download = filename
        a.style.display = 'none'
        a.href = blobUrl
        // 触发点击
        document.body.appendChild(a)
        a.click()
        // 然后移除
        document.body.removeChild(a)
      }
    
      // async 搭配 promise 使用
      const compress = async (file, maxSizeKB, succFunc) => {
        if (file.size > maxSizeKB * 1024) {
          let rate = 0 // 压缩率
    
          // 文件转图片
          const dataUrl = await fileToImgAsync(file)
    
          // 图片转画布
          const image = await loadImageAsync(dataUrl)
          // console.log(dataUrl, image)
    
          // 文件大小KB, file.size给的是字节Byte
          const fileSizeKB = file.size / 1024
          console.log(fileSizeKB)
    
          // 当图片大小超标,才进行压缩
          if (fileSizeKB > maxSizeKB) {
            // 计算压缩率
            rate = (fileSizeKB - maxSizeKB) / fileSizeKB
            console.log('压缩率:', rate)
            console.log('压缩后文件大小:', fileSizeKB * (1 - rate), 'kb')
          }
    
          // 纠正因子,不加会导致压缩出的文件太小
          const factor = 0.2
    
          // 画布执行压缩
          let canvas = document.createElement('canvas')
          let context = canvas.getContext('2d')
          const cvWidth = image.width * (1 - rate + factor)
          const cvHeight = image.height * (1 - rate + factor)
          console.log(image.width, image.height, cvWidth, cvHeight)
    
          canvas.height = cvHeight
          canvas.width = cvWidth
          context.clearRect(0, 0, cvWidth, cvHeight)
          context.drawImage(image, 0, 0, cvWidth, cvWidth)
    
          // 导出图片
          canvas.toBlob((blob) => {
    
            // 方式一:下载到本地
            const blobUrl = window.URL.createObjectURL(blob)
            downloadFileByBlob(blobUrl, file.name)
    
            // 方式二:生成网页可读取的对象
            // const newImage = new File([blob], file.name, { type: file.type });
            // succFunc(newImage)
          });
        }
      }
    
      return (
        <div className="App">
          <input type="file" id="file" onChange={handleChange} />
        </div>
      );
    }
    
    export default App;

     就这样吧,勉强能用,现在导出的都有800-900kb,还行吧

    参考:https://www.cnblogs.com/chenbeibei520/p/12534798.html

  • 相关阅读:
    每日英语:Yahoo's Rally: Made in China
    【Java线程】Callable和Future
    Throwable和Exception的区别
    Java异常分类
    牛客网上的剑指offer题目
    合并两个排序的链表
    反转链表
    链表中倒数第k个结点
    调整数组顺序使奇数位于偶数前面
    数值的整数次方
  • 原文地址:https://www.cnblogs.com/ww01/p/13328664.html
Copyright © 2011-2022 走看看