Files
Yi.Admin/Yi.Ai.Vue3/src/composables/chat/useImageCompression.ts
2026-01-31 23:38:39 +08:00

128 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ElMessage } from 'element-plus';
export interface CompressionLevel {
maxWidth: number;
maxHeight: number;
quality: number;
}
export const DEFAULT_COMPRESSION_LEVELS: CompressionLevel[] = [
{ maxWidth: 800, maxHeight: 800, quality: 0.6 },
{ maxWidth: 600, maxHeight: 600, quality: 0.5 },
{ maxWidth: 400, maxHeight: 400, quality: 0.4 },
];
/**
* 压缩图片
* @param file - 要压缩的图片文件
* @param maxWidth - 最大宽度
* @param maxHeight - 最大高度
* @param quality - 压缩质量 (0-1)
* @returns 压缩后的 Blob
*/
export function compressImage(
file: File,
maxWidth = 1024,
maxHeight = 1024,
quality = 0.8,
): Promise<Blob> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
let width = img.width;
let height = img.height;
if (width > maxWidth || height > maxHeight) {
const ratio = Math.min(maxWidth / width, maxHeight / height);
width = width * ratio;
height = height * ratio;
}
canvas.width = width;
canvas.height = height;
const ctx = canvas.getContext('2d')!;
ctx.drawImage(img, 0, 0, width, height);
canvas.toBlob(
(blob) => {
if (blob) {
resolve(blob);
}
else {
reject(new Error('压缩失败'));
}
},
file.type,
quality,
);
};
img.onerror = reject;
img.src = e.target?.result as string;
};
reader.onerror = reject;
reader.readAsDataURL(file);
});
}
/**
* 将 Blob 转换为 base64
* @param blob - Blob 对象
* @returns base64 字符串
*/
export function blobToBase64(blob: Blob): Promise<string> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = () => {
resolve(reader.result as string);
};
reader.onerror = reject;
reader.readAsDataURL(blob);
});
}
/**
* 尝试多级压缩直到满足大小限制
* @param file - 图片文件
* @param currentTotalLength - 当前已使用的总长度
* @param maxTotalLength - 最大允许总长度
* @returns 压缩结果或 null如果无法满足限制
*/
export async function tryCompressToLimit(
file: File,
currentTotalLength: number,
maxTotalLength: number,
compressionLevels = DEFAULT_COMPRESSION_LEVELS,
): Promise<{ blob: Blob; base64: string; estimatedLength: number } | null> {
for (const level of compressionLevels) {
const compressedBlob = await compressImage(
file,
level.maxWidth,
level.maxHeight,
level.quality,
);
const base64 = await blobToBase64(compressedBlob);
const estimatedLength = Math.floor(base64.length * 0.5);
if (currentTotalLength + estimatedLength <= maxTotalLength) {
return { blob: compressedBlob, base64, estimatedLength };
}
}
return null;
}
/**
* Composable: 使用图片压缩
*/
export function useImageCompression() {
return {
compressImage,
blobToBase64,
tryCompressToLimit,
DEFAULT_COMPRESSION_LEVELS,
};
}