使用ts实现一个压缩图片函数,支持设置最大尺寸及递归压缩

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;
    }
}