interface CompressOptions {
maxWidth?: number; // 图片最大宽度,默认 1920
maxHeight?: number; // 图片最大高度,默认 1080
quality?: number; // 压缩质量,0-1 之间的浮点数,默认为 0.9
maxSize?: number; // 最大文件大小,单位为字节,默认200KB
format?: string; // 输入的格式,默认为 jpg
}
/**
* 压缩图片
* @param file 要压缩的图片文件
* @param options 压缩选项
* @param recursiveCount 递归次数
* @returns Promise<Blob> 压缩后的 Blob 对象
*/
export async function compressImage(file: File | Blob, options: CompressOptions, recursiveCount = 0): Promise<Blob | null> {
const {maxWidth = 1920, maxHeight = 1080, quality = 0.9, maxSize = 204800, format = "jpg"} = options;
// 创建一个 Image 对象,用于读取图片文件
const img = new Image();
img.src = URL.createObjectURL(file);
// 等待图片加载完毕
await new Promise(resolve => {
img.onload = resolve;
});
// 获取图片的宽度和高度
const width = img.width;
const height = img.height;
// 如果图片尺寸小于等于最大尺寸,则不需要压缩,直接返回原始文件
if (width <= maxWidth && height <= maxHeight) {
return file;
}
// 计算压缩后的宽度和高度
let newWidth = width;
let newHeight = height;
if (width > maxWidth) {
newWidth = maxWidth;
newHeight = Math.floor(height * maxWidth / width);
}
if (newHeight > maxHeight) {
newWidth = Math.floor(newWidth * maxHeight / newHeight);
newHeight = maxHeight;
}
// 创建一个 Canvas 对象,用于绘制压缩后的图片
const canvas = document.createElement('canvas');
canvas.width = newWidth;
canvas.height = newHeight;
// 绘制压缩后的图片
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0, newWidth, newHeight);
// 将 Canvas 对象转换为 Blob 对象,并检查文件大小是否超过最大限制
const blob = await new Promise<Blob | null>(resolve => {
canvas.toBlob(resolve, `image/${format}`, quality);
});
if (blob && (maxSize && blob.size > maxSize) && recursiveCount < 10) {
// 如果文件大小超过最大限制,并且递归次数小于 10,则递归压缩
return compressImage(blob, options, recursiveCount + 1);
} else {
// 如果文件大小未超过最大限制,或者递归次数已经达到 10,则直接返回
return blob;
}
}