图片压缩保证让你看的明明白白

news/2024/11/16 15:35:22/文章来源:https://www.cnblogs.com/IwishIcould/p/18346907

场景

很多时候,都会遇见图片上传的场景。
在上传给服务器之前。
前端为了节省服务器的存储空间。
会对图片进行压缩。
下面我们来一起学习一下图片压缩。
图片压缩的步骤: 
1.选择图片。使用 <input type="file">来实现
2.将选择的图片显示出来。 获取到图片的base64,然后进行赋值
3.对图片进行进行压缩,如果压缩后的图比原图大,继续调用压缩函数,直到比原图小(如小30%)

选择图片并把图片显示出来

我们在读取文件的时候。
需要对图片的类型进行限制。
目前我们支持 'image/png', 'image/jpeg', 'image/gif', 'image/bmp'这些格式。
然后我们把图片转化为base64编码。
<style>#originPic{display: none;}
</style><div><input type="file" id="file" value="请选择图片" accept="image/*">
</div>
<div id="originPic"><span>原图:</span><img src="" id="originImg">
</div><script>let fileNode = document.getElementById('file')// 当用户选择文件的时候,就会触发readFile方法fileNode.addEventListener('change', readFile, false)// 装图片的盒子let originPicNode = document.getElementById('originPic')// 图片DOM节点let originImg = document.getElementById('originImg')// 常见的图片类型格式let canSelectPicTypeArr = ['image/png', 'image/jpeg', 'image/gif', 'image/bmp']function readFile(e){// 获取用户选择的文件let file = e.target.files[0]// 检查选择的图片是否符合要求if(canSelectPicTypeArr.includes(file.type)){// 创建一个文件对象let reader = new FileReader()reader.readAsDataURL(file)// 图片进行读取完成后,将图片的src赋值给img标签reader.onload = function(){originImg.src = reader.resultoriginPicNode.style.display = 'block'}}else{let str = canSelectPicTypeArr.join(',')alert('目前只支持图片格式' + str)}}
</script>

对图片进行压缩

要对图片进行压缩。
我们需要创建canvas标签,img标签。
将选择的图片绘制在canvas上,可以借助 ctx.drawImage函数来实现
最后通过 canvas.toDataURL来实现压缩。
canvas.toDataURL(picType, quality)
picType:表示的是图片类型
quality:表示的是压缩质量,取值范围0-1。默认是 0.92。值越小图压缩越大。
// 我们现在开始压缩图片
function assetImg(originPicInfo){// 创建canvas标签let canvasEle = document.createElement('canvas')// 创建img标签let imgEle = document.createElement('img')// 创建img标签imgEle.src = originPicInfo.originImgSrc// canvas的上下文let ctx= canvasEle.getContext('2d')// 读取图片imgEle.onload = ()=>{ // 获取图片的宽高const imgWidth = imgEle.widthconst imgHeight = imgEle.height// 将图片的宽高设置给canvascanvasEle.width = imgWidthcanvasEle.height = imgHeight// 把图片绘制在canvas上ctx.drawImage(imgEle, 0,0,imgWidth, imgHeight)// 生成压缩图片const assetPicBase64 = canvasEle.toDataURL(originPicInfo.imgType, 0.7)console.log('压缩后的图片', assetPicBase64)}
}

图片编码为base64后为啥还比原图要大?

要回答上面这个问题,就需要去了解base64的编码基本原理。
它的基本原理是:采用64个基本的 ASCII 码字符对数据进行重新编码。
首先他将源码数据拆分字节数组。
以3字节为一组,按顺序排列 24 位的数据。
然后再将24位数据分成4组,即每组6位。
在每组前面加两个00, 凑足一个字节。
这样就把一个 3 字节为一组的数据重新编码成了 4 个字节。
[读到这里,也许你应该猜到为啥图片通过base64编码后比原图要大的原因了]
当要编码的数据的字节数不是3的整倍数,
也就是说在分组时最后一组不 3个字节时。
会在最后一组填充1到2个0 字节。
并在最后编码完成时结尾添加1到2个 "="
我们通过编码规则可以得知,使用 Base64 编码。
原来的 3 个字节编码后将成为 4 个字节。
即字节增加了 33.3%,数据量相应变大。
所以 31KB 的数据通过 Base64 编码后大小大概为 31M*133.3%=42KM左右。
这一部分的参考相关链接:
https://www.ruanyifeng.com/blog/2008/06/base64.html
https://my.oschina.net/u/1422143/blog/702602

如何处理压缩后的图片比原图小?

我们刚刚知道了为啥base64编码后图片的大小比原图要大。
是因为: 3 个字节编码后将成为 4 个字节。所以比原图要大了。
现在我们只需要对图片的大小进行判断。
如果压缩后的图片比大,继续调用压缩函数。
如果比原图小,则停止下来。返回压缩后的图片的base64格式。

实现图片压缩功能

<style>#originPic{display: none;}#assetsBox{display: none;}
</style><div><input type="file" id="file" value="请选择图片" accept="image/*">
</div>
<div id="originPic"><p>原图:</p><img src="" id="originImg">
</div>
<div id="assetsBox"><p>压缩后的图:</p><img src="" id="assetsImg">
</div>
// 图片的类型
let fileType = ''
// 压缩后的图片(base64)
let compressImgSrc = ''
// 压缩质量值
let qualityValue =0.9
imgEle.onload = ()=>{ // ... 其他核心代码...// 调用图片压缩doPicCompress(canvasEle, originPicInfo.originImgSrc,fileType)console.log('压缩',compressImgSrc, qualityValue)// 将压缩的图片显示在页面上if(assetsBox){assetsBox.style.display = 'block'assetsImg.src = compressImgSrc}
}// 实现图片压缩
function doPicCompress(canvas, imgSrc, type){// 将图片转化为base64compressImgSrc = canvas.toDataURL(type, qualityValue)// 如果压缩后的图片大于原图,且图片质量还可以继续下调,则继续压缩if(compressImgSrc.length>=imgSrc.length && qualityValue>0.1){// 这里压缩的核心是下调图片的质量qualityValue = qualityValue - 0.1// 继续压缩doPicCompress(canvas, imgSrc, type)}
}


会不会出现多次压缩后,图片仍然比原图要大

有的小伙伴可能会说:
刚刚你说图片编码为base64后会比原图大。
如果是一张比较小的图片(30KB)左右。
会出现经过多次压缩后,图片仍然比原图要大的这种情况吗?
其实这一种是可能出现的。
原因的话是:图片本来就是较小的。编码base64后会比原图大。
借助toDataURL的压缩,并不一定会比原图小。
所以,图片较小,我们这种通过toDataURL进行压缩的方式就不好了。
当然处理前端进行图片压缩之后,后端也可以进行图片压缩。
下面我们通过node插件来简单看下

使用sharp来进行图片压缩

sharp:这个插件主要用于将常见格式的大图像转换为更小,
更适合网页使用的JPEG、PNG、WebP、GIF和AVIF格式的图像。
并且可以调整图像的尺寸。
安装: npm install sharp -S
具体使用的地址:https://www.npmjs.com/package/shap
// 引入依赖
const sharp = require('sharp');
// 传入图片路径为./js.png,进行压缩,然后输出新的yaSuoJS.png保存
sharp('./js.png').png({ quality: 50 }).toFile('yaSuoJS.png', (err, info) => {if (err) throw err;if(info){console.log(info);}});

全部代码

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title><style>#originPic{display: none;}#assetsBox{display: none;}</style>
</head>
<body><div><input type="file" id="file" value="请选择图片" accept="image/*"></div><div id="originPic"><p>原图:</p><img src="" id="originImg"></div><div id="assetsBox"><p>压缩后的图:</p><img src="" id="assetsImg"></div>
</body>
</html>
<script>let fileNode = document.getElementById('file')// 当用户选择文件的时候,就会触发readFile方法fileNode.addEventListener('change', readFile, false)// 装图片的盒子let originPicNode = document.getElementById('originPic')// 图片DOM节点let originImg = document.getElementById('originImg')// 压缩后装图片的DOM节点let assetsBox = document.getElementById('assetsBox')//  压缩后图片的DOM节点let assetsImg = document.getElementById('assetsImg')// 常见的图片类型格式let canSelectPicTypeArr = ['image/png', 'image/jpeg', 'image/gif', 'image/bmp']// 图片的类型let fileType = ''// 压缩后的图片(base64)let compressImgSrc = ''// 压缩质量值let qualityValue =0.2function readFile(e){// 获取用户选择的文件let file = e.target.files[0]fileType = file.type// 检查是否选择了符合要求的图片if(canSelectPicTypeArr.includes(fileType)){// 创建一个文件对象let reader = new FileReader()reader.readAsDataURL(file)reader.onload = function(){originImg.src = reader.resultconsole.log(reader.result, '压缩后的大小', )originPicNode.style.display = 'block'// 调用图片压缩这个函数assetImg({originImgSrc: originImg.src, quality:0.9, imgType: fileType})}}else{let str = canSelectPicTypeArr.join(',')alert('目前只支持图片格式' + str)}}// 我们现在开始压缩图片function assetImg(originPicInfo){// 创建canvas标签let canvasEle = document.createElement('canvas')// 创建img标签let imgEle = document.createElement('img')imgEle.src = originPicInfo.originImgSrclet ctx= canvasEle.getContext('2d')// 读取图片imgEle.onload = ()=>{ // 获取图片的宽高const imgWidth = imgEle.widthconst imgHeight = imgEle.height// 将图片的宽高设置给canvascanvasEle.width = imgWidthcanvasEle.height = imgHeight// 把图片绘制在canvas上ctx.drawImage(imgEle, 0,0,imgWidth, imgHeight)// 调用图片压缩doPicCompress(canvasEle, originPicInfo.originImgSrc,fileType)console.log(compressImgSrc, qualityValue, '压缩之后')// 将压缩的图片显示在页面上if(assetsBox){assetsBox.style.display = 'block'assetsImg.src = compressImgSrc}}}// 实现图片压缩function doPicCompress(canvas, imgSrc, type){// 将图片转化为base64compressImgSrc = canvas.toDataURL(type, qualityValue)// 如果压缩后的图片大于原图,且图片质量还可以继续下调,则继续压缩if(compressImgSrc.length>=imgSrc.length && qualityValue>0.1){// 这里压缩的核心是下调图片的质量qualityValue = qualityValue - 0.1// 继续压缩doPicCompress(canvas, imgSrc, type)}}
</script>

尾声

如果小伙伴觉得我写的不错的话,
可以给我点个赞吗?感谢了。
不说了,今天又是修改bug的一天

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/780792.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

学前准备工作

什么是计算机computer:全称电子计算机,简称电脑。 能够按照程序运行,自动、高速处理海量数据的现代化智能电子设备 由软件和硬件组成 常见形式有台式计算机,笔记本计算机,大型计算机等 广泛应用在:科学计算、数据处理、自动控制、计算机辅助设计、人工智能等领域。计算机…

多元时间序列分析统计学基础:基本概念、VMA、VAR和VARMA

多元时间序列是一个在大学课程中经常未被提及的话题。但是现实世界的数据通常具有多个维度,所以需要多元时间序列分析技术。在这文章我们将通过可视化和Python实现来学习多元时间序列概念。这里假设读者已经了解单变量时间序列分析。 1、什么是多元时间序列? 顾名思义,多元时…

wqs二分

wqs二分 用来处理一类带有限制的问题,如恰好选 \(k\) 个,本质是通过二分来规避这个选取数量的限制。 使用前提:原问题具有凹凸性。设 \(g_i\) 表示选 \(i\) 个物品的答案,那么所有 \((i, g_i)\) 点组成一个凸包,满足 \(g(k)\) 单调。 这类题目通常有以下特点:如果不限制选…

IDEA Sonar 扫描

1. 修改SonarQube-7.7\conf\sonar.properties数据库配置2. 启动SonarQube-7.7\bin\windows-x86-64\StartSonar.bat,打开 localhost:9000,账密 admin / admin3. pom文件配置:<profiles><profile><id>sonar</id><properties><sonar.host.url…

[AGC052B] Tree Edges XOR

好题,可以直接作为套路记录一下。 [AGC052B] Tree Edges XOR 题目大意: 给你一棵树,有奇数个点,每个边有边权 \(w_i\)。每次你可以选出一条边,将和这条边的所有相邻的边都异或这条边的边权,问你能否得到最终状态(操作次数不定)。 思路: 首先,上来会发现每次操作影响的…

[JVM] 应用诊断工具之java命令

0 序本章对java命令的使用、最佳实践进行全方位的总结。1 java命令 1.0 场景:查看版本方法1# java -version java version "1.8.0_261" Java(TM) SE Runtime Environment (build 1.8.0_261-b12) Java HotSpot(TM) 64-Bit Server VM (build 25.261-b12, mixed mode)方…

洛谷 P1127 词链——题解

洛谷P1127题解传送锚点摸鱼环节 词链 题目描述 如果单词 \(X\) 的末字母与单词 \(Y\) 的首字母相同,则 \(X\) 与 \(Y\) 可以相连成 \(X.Y\)。(注意:\(X\)、\(Y\) 之间是英文的句号 .)。例如,单词 dog 与单词 gopher,则 dog 与 gopher 可以相连成 dog.gopher。 另外还有一…

引领敏捷潮流:首届中国Scrum大会即将揭幕

中国「首届Scrum大会」将于2024年8月17日在上海大华虹桥假日酒店盛大召开。在全球数字化转型的浪潮中,敏捷已成为企业脱颖而出的关键。本次大会汇聚了Scrum领域的顶尖专家、实践者及企业领袖,共同探讨AI时代下的敏捷(Agile in the AI Age),深入探索智能时代的敏捷路径,掌…

解锁数学之美:VuePress博客如何优雅地呈现复杂公式

聊聊如何让 VuePress 显示数学公式块。聊聊如何让 VuePress 显示数学公式块。 ‍ 什么是数学公式块 如果你还不了解,可以先看看我的 Markdown 教程——Markdown 与数学公式。 ‍ ‍ 安装依赖 相关插件有很多,我这里选择的是 markdown-it-mathjax3​: npm i markdown-it-math…

【攻防】一个关于内网渗透过程的小技巧

在我们打攻防进行内网渗透的过程中,很多时候需要去收集内网的密码字典再对内网主机进行批量的密码喷洒。 这里密码获取的手段有很多,例如: 1、mimikatz抓取入口主机密码 2、翻查入口主机中关于密码的文件(例如数据库密码) 3、web系统的密码 4、自己构造目标的密码字典(xx…