ts前端实现图片压缩

264 阅读3分钟

一、背景

前端接口请求被拦截,需要经过一个拦截转发到后端,在拦截行为众,图片base64太大需要在前端进行压缩行为 加快流程

二、实现

const fileToDataURL = (file: Blob): Promise<any> => {
    return new Promise((resolve) => {
        const reader = new FileReader();
        reader.onloadend = (e) => { resolve((e.target!).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); });
};

const canvastoBase64 = (canvas: HTMLCanvasElement, type: string, encoderOptions: number): Promise<string> => {
    return new Promise((resolve) => { resolve(canvas.toDataURL(type, encoderOptions)); });
};

/**
 * 图片压缩方法
 *
 * @param {object}  file 图片文件
 * @param {string} type 想压缩成的文件类型
 * @param {Number} quality 压缩质量参数
 * @param flleType 需要导出的格式 
 * @returns 压缩后的新图片或者base64
 */
interface PictureConfig {
    file: File;
    type?: string; 
    quality?:number, 
    flleType?: 'blob' | 'base64';
    maxWidth?: number;
    maxHeight?: number;
}
/**
 * 图片缩放函数
 * @param config 
 * @returns {string | blob} 图片base4或者file类型
 */
const compressionPicture = async (config: PictureConfig): Promise<string | File> => {
    let defaultConfig = {
        type:'image/jpeg',
        quality: 0.5,
        flleType:'base64',
        maxWidth: 600,
        maxHeight: 600,
    }
    defaultConfig = Object.assign(defaultConfig,config);

    // 最大尺寸限制
    const maxWidth = defaultConfig.maxWidth,maxHeight = defaultConfig.maxHeight;
    const fileName = config.file.name;
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d')!;
    const base64 = await fileToDataURL(config.file);
    const img = await dataURLToImage(base64);
    const originWidth = img.width, originHeight = img.height;
    let 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));
        }
    }
    canvas.width = targetWidth;
    canvas.height = targetHeight;

    context.imageSmoothingEnabled = true;
    // 清除画布
    context.clearRect(0, 0, targetWidth, targetHeight);
    context.drawImage(img, 0, 0, targetWidth, targetHeight);
    if (defaultConfig.flleType === 'blob') {
        const blob = await canvastoFile(canvas, defaultConfig.type, defaultConfig.quality) as Blob;
        const newFile = await new File([blob], fileName, {
            type: defaultConfig.type,
        });
        return newFile;
    }
    const dataUrl = await canvastoBase64(canvas, defaultConfig.type, defaultConfig.quality)!;
    return dataUrl;
};

export default compressionPicture;

原理: 利用canvas实例身上的toBlob或者 toDataURL 方法进行压缩动作,输出文件

步骤 1. 将二进制文件对象处理为base64 (利用FileReader 去异步读取文件数据)

FileReader实例提供的方法如下

readAsArrayBuffer(file)
按字节读取文件内容,结果用ArrayBuffer对象表示
readAsBinaryString(file)
按字节读取文件内容,结果为文件的二进制串
readAsDataURL(file)
读取文件内容,结果用data:url的字符串形式表示 ---->我们需要给
readAsText(file,encoding)
按字符读取文件内容,结果用字符串形式表示
abort()
终止文件读取操作

image.png 步骤2.  将步骤一输出的base64 创造  img元素 并赋值于src 输出img实例

image.png

步骤3.  将步骤二输出的img实例 通过canvas绘制成图像 并且按照所需图片要求对图片进行裁切 (等比例缩放)

image.png

步骤4. 利用上述步骤生成的canvas实例 调用其身上的toBlob( 输出 blob二进制文件对象 方便给formData 传递文件给后端 或者toDataURL( 输出base64  输出进行压缩后的图片格式

image.png

  使用:

      传入需要被裁切的图片file,以及想要输出的图片格式 jpg,png等 以及需要生成图片的宽高 以及 调用函数返回的数据类格式(blob || base64)

三、总结

该函数可以在需要前端进行图片压缩的时候对图片进行一定程度的压缩,可以设置输出数据的格式

但是不能指定压缩后生成的文件大小 所以存在一定的局限性和优化空间