图片压缩的实现
一、压缩原理
前端实现图片压缩,主要是通过将原图绘制成 Canvas,然后利用 Canvas 提供的 toDataURL() 或 toBlob() 方法,传入质量参数,进行图片压缩。
1、toDataURL()
浏览器提供的 toDataURL 方法可以将 Canvas 图像对应的 data URI(即 base64 地址)
HTMLCanvasElement.toDataURL(mimeType, quality);
参数:
mimeType
(可选)String:表示需要转换的图像的 mimeType 类型,默认为 image/png,可以是 image/jpeg、image/webp 等;quality
(可选)Number:表示转换的图片质量,默认为 0.92,此参数需要图片类型为 image/jpeg 或 image/webp。
返回值:base64 data 图片数据。
2、toBlob()
浏览器提供的 toBlob 方法可以将 Canvas 图像对应的 Blob 对象。
HTMLCanvasElement.toBlob(callback, mimeType, quality);
参数:
callback
(必须)Function:执行成功后的回调方法,参数表示转换的 Blob 对象;mimeType
(可选)String:表示需要转换的图像的 mimeType 类型,默认为 image/png,可以是 image/jpeg、image/webp 等;quality
(可选)Number:表示转换的图片质量,默认为 0.92,此参数需要图片类型为 image/jpeg 或 image/webp。
返回值:无
二、通过手写实现图片压缩
这里以 React 上传图片并自动压缩为例。
1、实现过程
- 图片上传并进行回显
- 上传图片后创建 Canvas 根据原图绘制图片,注意 Canvas 像素要与原图保持同步
- 通过 toDataURL 或 toBlob 方法对图片进行压缩,并拿到压缩后图片的 url
2、实现源码
- App.jsx
- App.css
import React, { useState, useRef } from 'react'
import './App.css'
const App = () => {
// 获取原图及 Canvas DOM
const curImg = useRef(null)
// 原图 url 及大小
const [imgSrc, setImgSrc] = useState('')
const [imgSize, setImgSize] = useState(0)
// 压缩图的 url 及大小
const [compressImgSrc, setCompressImgSrc] = useState('')
const [compressImgSize, setCompressImgSize] = useState(0)
// 绘制 Canvas 函数
const draw = () => {
const image = curImg.current
// 创建 canvas 绘制上下文
const cvs = document.createElement('canvas')
const context = cvs.getContext('2d')
// 原图成功加载后绘制到 Canvas,并转为压缩后的图片
image.onload = () => {
// 计算拿到原图原始宽高
cvs.width = image.naturalWidth
cvs.height = image.naturalHeight
// 绘制原图至 Canvas,像素与原图保持同步
context.drawImage(image, 0, 0, image.naturalWidth, image.naturalHeight)
// 将 canvas 转为图片,压缩质量参数为 0.8
cvs.toBlob(
(blob) => {
setCompressImgSrc(URL.createObjectURL(blob))
setCompressImgSize((blob.size / 1024 / 1024).toFixed(1))
},
'image/jpeg',
0.8
)
}
}
// 点击上传图片并进行图片回显
const uploadImage = (event) => {
let curFile = event.currentTarget.files[0]
// 限制图片大小
if (curFile.size > 10 * 1024 * 1024) {
alert('图片大于 10M 请重新上传')
} else {
setImgSize((curFile.size / 1024 / 1024).toFixed(1))
// 使用 FileReader 进行已上传文件的读取
let reader = new FileReader()
reader.onload = (event) => {
let curImgUrl = event.currentTarget.result
setImgSrc(curImgUrl)
// 根据上传的图片绘制 canvas
draw()
}
reader.readAsDataURL(curFile)
}
}
return (
<>
<input type="file" accept="image/*" onChange={uploadImage} />
<div className="container">
<div className="item">
<section className="img-wrap">
<img
ref={curImg}
src={imgSrc}
width="200"
height="120"
alt="图片"
/>
</section>
<p>原图:{imgSize}MB</p>
</div>
<div className="item">
<section className="img-wrap">
<img
src={compressImgSrc}
width="200"
height="120"
alt="图片"
/>
</section>
<p>压缩后:{compressImgSize}MB</p>
</div>
</div>
</>
)
}
export default App
.container {
display: flex;
margin-top: 20px;
}
.item {
margin-right: 20px;
}
.img-wrap {
width: 200px;
height: 200px;
border: 1px solid #e5e5e6;
}
3、实现效果
上传前:
上传后:
三、通过 Compressor.js 库实现
Compressor.js 是用 HTMLCanvasElement.toBlob
进行压缩的 JavaScript 库,可用在上传之前预压缩客户端的图像文件。
1、安装
npm install compressorjs
2、使用方式
new Compressor(file[, options])
参数:
- file(必须):需要压缩的 File/Blob 格式图像文件;
- options(可选):压缩参数。
3、示例
与上面手写实现压缩的代码一样,但使用 Compressor 可以替代 Canvas 转换手写的代码:
import React, { useState, useRef } from 'react'
import './App.css'
import Compressor from 'compressorjs'
const App = () => {
// 获取原图及 Canvas DOM
const curImg = useRef(null)
// 原图 url 及大小
const [imgSrc, setImgSrc] = useState('')
const [imgSize, setImgSize] = useState(0)
// 压缩图的 url 及大小
const [compressImgSrc, setCompressImgSrc] = useState('')
const [compressImgSize, setCompressImgSize] = useState(0)
// 设置回显压缩图
const setCompressImg = (blob) => {
setCompressImgSrc(URL.createObjectURL(blob))
setCompressImgSize((blob.size / 1024 / 1024).toFixed(1))
}
// 上传显示图片操作
const uploadImage = (event) => {
let curFile = event.currentTarget.files[0]
// 限制图片大小
if (curFile.size > 10 * 1024 * 1024) {
alert('图片大于 10M 请重新上传')
} else {
setImgSize((curFile.size / 1024 / 1024).toFixed(1))
// 回显原图
let reader = new FileReader()
reader.onload = (event) => {
let curImgUrl = event.currentTarget.result
setImgSrc(curImgUrl)
}
reader.readAsDataURL(curFile)
// 直接使用 Compressor 进行图片压缩
new Compressor(curFile, {
quality: 0.8,
success(result) {
setCompressImg(result)
},
error(err) {
setCompressImg(curFile)
console.log(err.message)
},
})
}
}
return (
<>
<input type="file" accept="image/*" onChange={uploadImage} />
<div className="container">
<div className="item">
<section className="img-wrap">
<img
ref={curImg}
src={imgSrc}
alt="图片"
/>
</section>
<p>原图:{imgSize}MB</p>
</div>
<div className="item">
<section className="img-wrap">
<img
src={compressImgSrc}
alt="图片"
/>
</section>
<p>压缩后:{compressImgSize}MB</p>
</div>
</div>
</>
)
}
export default App
上传前:
上传后: