写点什么

JS 图片压缩

  • 2021-03-20
  • 本文字数:4459 字

    阅读完需:约 15 分钟

JS 图片压缩

AI 大模型超全落地场景&金融应用实践,8 月 16 - 19 日 FCon x AICon 大会联诀来袭、干货翻倍!

前言


说起图片压缩,大家想到的或者平时用到的很多工具都可以实现,例如,客户端类的有图片压缩工具 PPDuck3, JS 实现类的有插件 compression.js ,亦或是在线处理类的 OSS 上传,文件上传后,在访问文件时中也有图片的压缩配置选项,不过,能不能自己撸一套  JS 实现的图片压缩代码呢?当然可以,那我们先来理一下思路。


压缩思路


涉及到 JS 的图片压缩,我的想法是需要用到 Canvas 的绘图能力,通过调整图片的分辨率或者绘图质量来达到图片压缩的效果,实现思路如下:


  • 获取上传 Input 中的图片对象 File

  • 将图片转换成 base64 格式

  • base64 编码的图片通过 Canvas 转换压缩,这里会用到的 Canvas 的 drawImage 以及 toDataURL 这两个 Api,一个调节图片的分辨率的,一个是调节图片压缩质量并且输出的,后续会有详细介绍

  • 转换后的图片生成对应的新图片,然后输出


优缺点介绍


不过 Canvas 压缩的方式也有着自己的优缺点:


  • 优点:实现简单,参数可以配置化,自定义图片的尺寸,指定区域裁剪等等。

  • 缺点:只有 jpeg 、webp 支持原图尺寸下图片质量的调整来达到压缩图片的效果,其他图片格式,仅能通过调节尺寸来实现


代码实现


<template>  <div class="container">    <input type="file" id="input-img" @change="compress" />    <a :download="fileName" :href="compressImg" >普通下载</a>    <button @click="downloadImg">兼容 IE 下载</button>    <div>      <img :src="compressImg" />    </div>  </div></template><script>export default {  name: 'compress',  data: function() {    return {      compressImg: null,      fileName: null,    };  },  components: {},  methods: {    compress() {      // 获取文件对象      const fileObj = document.querySelector('#input-img').files[0];      // 获取文件名称,后续下载重命名      this.fileName = `${new Date().getTime()}-${fileObj.name}`;      // 获取文件后缀名      const fileNames = fileObj.name.split('.');      const type = fileNames[fileNames.length-1];      // 压缩图片      this.handleCompressImage(fileObj, type);    },    handleCompressImage(img, type) {      const vm = this;      let reader = new FileReader();      // 读取文件      reader.readAsDataURL(img);      reader.onload = function(e) {        let image = new Image(); //新建一个img标签        image.src = e.target.result;        image.onload = function() {          let canvas = document.createElement('canvas');          let context = canvas.getContext('2d');          // 定义 canvas 大小,也就是压缩后下载的图片大小          let imageWidth = image.width; //压缩后图片的大小          let imageHeight = image.height;          canvas.width = imageWidth;          canvas.height = imageHeight;                    // 图片不压缩,全部加载展示          context.drawImage(image, 0, 0);          // 图片按压缩尺寸载入          // let imageWidth = 500; //压缩后图片的大小          // let imageHeight = 200;          // context.drawImage(image, 0, 0, 500, 200);          // 图片去截取指定位置载入          // context.drawImage(image,100, 100, 100, 100, 0, 0, imageWidth, imageHeight);          vm.compressImg = canvas.toDataURL(`image/${type}`);        };      };    },    // base64 图片转 blob 后下载    downloadImg() {      let parts = this.compressImg.split(';base64,');      let contentType = parts[0].split(':')[1];      let raw = window.atob(parts[1]);      let rawLength = raw.length;      let uInt8Array = new Uint8Array(rawLength);      for(let i = 0; i < rawLength; ++i) {        uInt8Array[i] = raw.charCodeAt(i);      }      const blob = new Blob([uInt8Array], {type: contentType});      this.compressImg = URL.createObjectURL(blob);      if (window.navigator.msSaveOrOpenBlob) {        // 兼容 ie 的下载方式        window.navigator.msSaveOrOpenBlob(blob, this.fileName);      }else{        const a = document.createElement('a');        a.href = this.compressImg;        a.setAttribute('download', this.fileName);        a.click();      }    },  }};</script>
复制代码


上面的代码是可以直接拿来看效果的,不喜欢用 Vue 的也可以把代码稍微调整一下,下面开始具体分解一下代码的实现思路


Input 上传 File 处理


将 File 对象通过 FileReader 的 readAsDataURL 方法转换为 URL 格式的字符串(base64 编码)

const fileObj = document.querySelector('#input-img').files[0];let reader = new FileReader();// 读取文件reader.readAsDataURL(fileObj);
复制代码


Canvas 处理 File 对象

建立一个 Image 对象,一个 canvas 画布,设定自己想要下载的图片尺寸,调用 drawImage 方法在 canvas 中绘制上传的图片

let image = new Image(); //新建一个img标签image.src = e.target.result;let canvas = document.createElement('canvas');let context = canvas.getContext('2d');context.drawImage(image, 0, 0);
复制代码


Api 解析:drawImage


context.drawImage(img, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);
复制代码

img

就是图片对象,可以是页面上获取的 DOM 对象,也可以是虚拟 DOM 中的图片对象。

dx、dy、dWidth、dHeight

表示在 canvas 画布上规划出一片区域用来放置图片,dx, dy 为绘图位置在 Canvas 元素的 X 轴、Y 轴坐标,dWidth, dHeight 指在 Canvas 元素上绘制图像的宽度和高度(如果不说明, 在绘制时图片的宽度和高度不会缩放)。


sx、sy、swidth、sheight

这 4 个参数是用来裁剪源图片的,表示图片在 canvas 画布上显示的大小和位置。sx, sy 表示在源图片上裁剪位置的 X 轴、Y 轴坐标,然后以 swidth, sheight 尺寸来选择一个区域范围,裁剪出来的图片作为最终在 Canvas 上显示的图片内容( swidth, sheight 不说明的情况下,整个矩形(裁剪)从坐标的 sx 和 sy 开始,到图片的右下角结束)。


以下为图片绘制的实例:

context.drawImage(image, 0, 0, 100, 100);context.drawImage(image, 300, 300, 200, 200);context.drawImage(image, 0, 100, 150, 150, 300, 0, 150, 150);
复制代码


Api 中奇怪之处在于,sx、sy、swidth、sheight 为选填参数,但位置在 dx、dy、dWidth、dHeight 之前。


Canvas 输出图片

调用 canvas 的 toDataURL 方法可以输出 base64 格式的图片。

canvas.toDataURL(`image/${type}`);
复制代码


Api 解析:toDataURL

canvas.toDataURL(type, encoderOptions);
复制代码

type 可选

图片格式,默认为 image/png。

encoderOptions 可选

在指定图片格式为 image/jpeg 或 image/webp 的情况下,可以从 0 到 1 的区间内选择图片的质量。如果超出取值范围,将会使用默认值 0.92。其他参数会被忽略。


a 标签的下载

调用 <a> 标签的 download 属性,即可完成图片的下载。


Api 解析:download

// href 下载必填<a download="filename" href="href"> 下载 </a>
复制代码

filename

选填,规定作为文件名来使用的文本。

href

文件的下载地址。


非主流浏览器下载处理


到此可以解决 Chroma 、 Firefox 和 Safari(自测支持) 浏览器的下载功能,因为 IE 等浏览器不支持 download 属性,所以需要进行其他方式的下载,也就有了代码中的后续内容

// base64 图片转 blob 后下载downloadImg() {  let parts = this.compressImg.split(';base64,');  let contentType = parts[0].split(':')[1];  let raw = window.atob(parts[1]);  let rawLength = raw.length;  let uInt8Array = new Uint8Array(rawLength);  for(let i = 0; i < rawLength; ++i) {    uInt8Array[i] = raw.charCodeAt(i);  }  const blob = new Blob([uInt8Array], {type: contentType});  this.compressImg = URL.createObjectURL(blob);  if (window.navigator.msSaveOrOpenBlob) {    // 兼容 ie 的下载方式    window.navigator.msSaveOrOpenBlob(blob, this.fileName);  }else{    const a = document.createElement('a');    a.href = this.compressImg;    a.setAttribute('download', this.fileName);    a.click();  }}
复制代码
  • 将之前 canvas 生成的 base64 数据拆分后,通过 atob 方法解码

  • 将解码后的数据转换成 Uint8Array 格式的无符号整形数组

  • 转换后的数组来生成一个 Blob 数据对象,通过 URL.createObjectURL(blob) 来生成一个临时的 DOM 对象

  • 之后 IE 类浏览器可以调用 window.navigator.msSaveOrOpenBlob 方法来执行下载,其他浏览器也可以继续通过 <a> 标签的 download 属性来进行下载


Api 解析:atob

base-64 解码使用方法是 atob()。

window.atob(encodedStr)
复制代码

encodedStr

必需,是一个通过 btoa() 方法编码的字符串,btoa() 是 base64 编码的使用方法。


Api 解析:Uint8Array

new Uint8Array(length)
复制代码

length

创建初始化为 0 的,包含 length 个元素的无符号整型数组。

Api 解析:Blob

Blob 对象表示一个不可变、原始数据的类文件对象。

// 构造函数允许通过其它对象创建 Blob 对象new Blob([obj],{type:createType}) 
复制代码

obj

字符串内容

createType

要构造的类型

兼容性 IE 10 以上


Api 解析:createObjectURL

静态方法会创建一个 DOMString。

objectURL = URL.createObjectURL(object);
复制代码

object

用于创建 URL 的 File 对象、Blob 对象或者 MediaSource 对象。


Api 解析:window.navigator

// 官方已不建议使用的文件下载方式,仅针对 ie 且兼容性 10 以上// msSaveBlob 仅提供下载// msSaveOrOpenBlob 支持下载和打开window.navigator.msSaveOrOpenBlob(blob, fileName);
复制代码

blob

要下载的 blob 对象

fileName

下载后命名的文件名称。


总结


本文仅针对图片压缩介绍了一些思路,简单的使用场景可能如下介绍,当然也会引申出来更多的使用场景,这些还有待大家一起挖掘。


  • 上传存储图片如果需要对文件大小格式有要求的,可以统一压缩处理图片

  • 前台页面想要编辑图片,可以在 Canvas 处理图片的时候,加一些其他逻辑,例如添加文字,剪裁,拼图等等操作


当然温馨提示:因部分接口有 IE 兼容性问题,IE 浏览器方面,仅能支持 IE 10 以上版本进行下载。



头图:Unsplash

作者:Jeson

原文:https://mp.weixin.qq.com/s/EVD7RQdMpwzEzQCwv8GMwA

原文:JS 图片压缩

来源:政采云前端团队 - 微信公众号 [ID:Zoo-Team]

转载:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

2021-03-20 00:133568

评论

发布
暂无评论
发现更多内容

百度智能云将大模型引入网络故障定位的智能运维实践

百度Geek说

网络 智能运维 大模型 企业号 7 月 PK 榜

Databend 开源周报第 153 期

Databend

堡垒机小知识之堡垒机使用范围讲解

行云管家

网络安全 数据安全 等保 堡垒机

【YashanDB知识库】收集分区表统计信息采样率小于1导致SQL执行计划走偏

YashanDB

yashandb 崖山数据库 崖山DB

好用的Mac应用程序清理卸载工具:App Cleaner & Uninstall

你的猪会飞吗

Mac 软件

【YashanDB知识库】数据变化率超过阈值统计信息失效

YashanDB

yashandb 崖山数据库 崖山DB

性能测试:性能测试报告

霍格沃兹测试开发学社

在 JeecgBoot 项目中基于 Vue 3 配置多页面入口

JEECG低代码

前端开发 Vue3

【堡垒机小知识】堡垒机可以定期自动修改主机密码吗?

行云管家

等保 堡垒机 等级保护

前端打包工具Mako架构解析|得物技术

得物技术

rust webassembly web前端 构建工具 企业号2024年7月PK榜

Lazada商品详情数据接口API使用教程:轻松集成,高效管理

tbapi

lazada商品详情数据接口 lazada API lazada商品数据采集 lazada

爆火的无人驾驶萝卜快跑,背后有哪些隐忧?

博文视点Broadview

深入探索孔夫子旧书网商品详情数据接口:解锁二手书市场的无限可能

tbapi

孔夫子网 孔夫子商品详情数据接口 孔夫子商品数据采集 孔夫子网API

PostgreSQL可视化表结构设计

NineData

postgresql 表结构 可视化工具 NineData SQL编写

LED租赁系列与舞台“融为一体”

Dylan

技术 灯光秀 LED LED display LED显示屏

基于MindSpore实现BERT对话情绪识别

华为云开发者联盟

人工智能 深度学习 华为云 华为云开发者联盟 企业号2024年7月PK榜

IPQ9570 vs. IPQ9574: A Technical Comparison

wifi6-yiyi

5G WiFi7 6G

性能测试:性能测试报告

测吧(北京)科技有限公司

测试

人工智能与机器人学:智能自主系统的融合与未来

天津汇柏科技有限公司

人工智能 机器学习人工智能

【YashanDB知识库】手动停止统计信息自动收集任务导致的性能变差

YashanDB

yashandb 崖山数据库 崖山DB

性能测试:性能测试报告

测试人

软件测试

AI+资源数据分析运营助手

鲸品堂

AI 运维 资源管理 企业号2024年7月PK榜

【7月27日丨上海】Elastic & 阿里云邀您参加 AI 搜索 Tech Day

阿里云大数据AI技术

人工智能 elastic

淘宝/天猫商品详情API接口在跨境电商数据采集中的应用

技术冰糖葫芦

API 安全 API 文档 API 开发 API 协议

【YashanDB知识库】数据库审计shutdown immediate操作导致数据库异常退出

YashanDB

yashandb 崖山数据库 崖山DB

Showdown: IPQ5332 vs IPQ8072 – Who Will Reign Supreme in IndustrialWiFi?

wallyslilly

IPQ8072 IPQ5332

【YashanDB知识库】同时设置默认值和非空约束时报错YAS-02070

YashanDB

yashandb 崖山数据库 崖山DB

在MoneyPrinterPlus中使用本地chatTTS语音模型

程序那些事

工具 程序那些事 AIGC MoneyPrinterPlus

移动端风险频发,设备指纹牢筑安全防线

芯盾时代

终端安全 移动安全

JS 图片压缩_语言 & 开发_政采云前端团队_InfoQ精选文章