diff --git a/src/components/chat/ChatInput.tsx b/src/components/chat/ChatInput.tsx index 94eea31..cd56ac1 100644 --- a/src/components/chat/ChatInput.tsx +++ b/src/components/chat/ChatInput.tsx @@ -69,12 +69,13 @@ export function ChatInput(): React.JSX.Element { const attachment: Attachment = { id: `att_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`, file, type }; if (type === 'image') { - // 图片转 base64 data URL - attachment.preview = await new Promise((resolve) => { + // 图片转 base64 data URL → 压缩(限制 1024px, JPEG 0.7) + const dataUri = await new Promise((resolve) => { const reader = new FileReader(); reader.onload = () => resolve(reader.result as string); reader.readAsDataURL(file); }); + attachment.preview = await compressImage(dataUri, 1024, 0.7); } else if (type === 'text') { // 文本文件读取内容 attachment.textContent = await new Promise((resolve) => { @@ -309,3 +310,39 @@ function AttachmentPreview({ attachment, onRemove }: { attachment: Attachment; o ); } + +/** + * 压缩图片:限制最大边长,输出 JPEG base64。 + * 小图(< 500KB 且尺寸未超标)直接返回原图,避免重复编码。 + * + * @see AgnesAIDesktop — 已验证 Agnes AI 可正常处理压缩后的图片 + */ +function compressImage( + dataUri: string, + maxSize: number, + quality: number, +): Promise { + return new Promise((resolve, reject) => { + const img = new Image(); + img.onload = () => { + let { width, height } = img; + if (width <= maxSize && height <= maxSize && dataUri.length < 500 * 1024) { + resolve(dataUri); + return; + } + if (width > height) { + if (width > maxSize) { height = Math.round((height * maxSize) / width); width = maxSize; } + } else { + if (height > maxSize) { width = Math.round((width * maxSize) / height); height = maxSize; } + } + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + const ctx = canvas.getContext('2d')!; + ctx.drawImage(img, 0, 0, width, height); + resolve(canvas.toDataURL('image/jpeg', quality)); + }; + img.onerror = () => reject(new Error('图片加载失败')); + img.src = dataUri; + }); +}