图片压缩流程
压缩图片基本流程
- input 读取到 文件 ,使用 FileReader 将其转换为 base64 编码
- 新建 img ,使其 src 指向刚刚的 base64
- 新建 canvas ,将 img 画到 canvas 上
- 利用 canvas.toDataURL/toBlob 将 canvas 导出为 base64 或 Blob
- 将 base64 或 Blob 转化为 File 将这些步骤逐个拆解,我们会发现似乎在canvas.toDataURL时涉及到图片质量,那咱们就从这里下手。
准备
HTMLCanvasElement.toDataURL()
HTMLCanvasElement.toDataURL()
方法返回一个包含图片展示的 data URI 。
可以使用 type 参数其类型,默认为 PNG 格式。
图片的分辨率为96dpi。
- 如果画布的高度或宽度是0,那么会返回字符串
data:,
。 - 如果传入的类型非
image/png
,但是返回的值以data:image/png
开头,那么该传入的类型是不支持的。
语法
canvas.toDataURL(type, encoderOptions);
参数
- type 可选 图片格式,默认为 image/png
- encoderOptions 可选 在指定图片格式为
image/jpeg
或image/webp
的情况下,可以从 0 到 1 的区间内选择图片的质量。 如果超出取值范围,将会使用默认值0.92
。其他参数会被忽略。 Chrome支持image/webp
类型。
JS
压缩比例在0.9
以下都会明显的文件变小,建议0.8
。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>图片压缩</title>
</head>
<body>
<input type="file" name="pic" id="pic">
<br/><br/>
<img src="" alt="" class="imgleft"/>
<img src="" alt="" class="imgright"/>
<script>
/**
* 压缩图片方法
* @param {file} file 文件
* @param {Number} quality 图片质量(取值0-1之间默认0.92)
*/
function compressImg(file, quality) {
var qualitys = 0.52
if (parseInt((file.size / 1024).toFixed(2)) < 1024) {
qualitys = 0.85
}
if (5 * 1024 < parseInt((file.size / 1024).toFixed(2))) {
qualitys = 0.92
}
if (quality) {
qualitys = quality
}
if (file[0]) {
return Promise.all(Array.from(file).map(e => compressImg(e,
qualitys))) // 如果是 file 数组返回 Promise 数组
} else {
return new Promise((resolve) => {
if ((file.size / 1024).toFixed(2) < 300) {
resolve({
file: file
})
} else {
const fileReader = new FileReader() // 创建 FileReader
fileReader.onload = ({target: {result: src}}) => {
const image = new Image() // 创建 img 元素
image.onload = async () => {
const mCanvas = document.createElement('canvas') // 创建 canvas 元素
const mCtx = mCanvas.getContext('2d')
var targetWidth = image.width
var targetHeight = image.height
var originWidth = image.width
var originHeight = image.height
if (1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 10 * 1024) {
var maxWidth = 1600
var maxHeight = 1600
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
if (10 * 1024 <= parseInt((file.size / 1024).toFixed(2)) && parseInt((file.size / 1024).toFixed(2)) <= 20 * 1024) {
maxWidth = 1400
maxHeight = 1400
targetWidth = originWidth
targetHeight = originHeight
// 图片尺寸超过的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
// 更宽,按照宽度限定尺寸
targetWidth = maxWidth
targetHeight = Math.round(maxWidth * (originHeight / originWidth))
} else {
targetHeight = maxHeight
targetWidth = Math.round(maxHeight * (originWidth / originHeight))
}
}
}
mCanvas.width = targetWidth
mCanvas.height = targetHeight
mCtx.clearRect(0, 0, targetWidth, targetHeight)
mCtx.drawImage(image, 0, 0, targetWidth, targetHeight) // 绘制 canvas
const canvasURL = mCanvas.toDataURL('image/jpeg', qualitys)
const buffer = atob(canvasURL.split(',')[1])
let length = buffer.length
const bufferArray = new Uint8Array(new ArrayBuffer(length))
while (length--) {
bufferArray[length] = buffer.charCodeAt(length)
}
const miniFile = new File([bufferArray], file.name, {
type: 'image/jpeg'
})
resolve({
beforeFile: file,
afterFile: miniFile,
beforeSrc: src,
afterSrc: canvasURL,
beforeKB: Number((file.size / 1024).toFixed(2)),
afterKB: Number((miniFile.size / 1024).toFixed(2))
})
}
image.src = src
}
fileReader.readAsDataURL(file)
}
})
}
}
// 获得元素对象
var fileDom = document.querySelector('#pic');
var imgleft = document.querySelector('.imgleft');
var imgright = document.querySelector('.imgright');
// 当上传框发生变化的时候,显示对应的图片
fileDom.onchange = async function () {
imgleft.src = window.URL.createObjectURL(fileDom.files[0]);
let result = await compressImg(fileDom.files[0],0.8)
console.info(result);
imgright.src = result.afterSrc;
}
</script>
<style>
.imgleft {
width: 500px;
}
.imgright{
width: 500px;
}
</style>
</body>
</html>
TS版本
代码语言:javascript复制const fileToDataURL = (file: Blob): Promise<any> => {
return new Promise((resolve) => {
const reader = new FileReader()
reader.onloadend = (e) => resolve((e.target as FileReader).result)
reader.readAsDataURL(file)
})
}
const dataURLToImage = (dataURL: string): Promise<HTMLImageElement> => {
return new Promise((resolve) => {
const img = new Image()
img.onload = () => resolve(img)
img.src = dataURL
})
}
const canvastoFile = (canvas: HTMLCanvasElement, type: string, quality: number): Promise<Blob | null> => {
return new Promise((resolve) => canvas.toBlob((blob) => resolve(blob), type, quality))
}
/**
* 图片压缩方法
* @param {Object} file 图片文件
* @param {String} type 想压缩成的文件类型
* @param {Nubmber} quality 压缩质量参数
* @returns 压缩后的新图片
*/
export const compressionFile = async(file, type = 'image/jpeg', quality = 0.5) => {
const fileName = file.name
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d') as CanvasRenderingContext2D
const base64 = await fileToDataURL(file)
const img = await dataURLToImage(base64)
canvas.width = img.width
canvas.height = img.height
context.clearRect(0, 0, img.width, img.height)
context.drawImage(img, 0, 0, img.width, img.height)
const blob = (await canvastoFile(canvas, type, quality)) as Blob // quality:0.5可根据实际情况计算
const newFile = await new File([blob], fileName, {
type: type
})
return newFile
}