import { CACHE_URL_PREFIX, UPLOAD_URL } from "@/app/constant"; import { RequestMessage } from "@/app/client/api"; export function compressImage(file: Blob, maxSize: number): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.onload = (readerEvent: any) => { const image = new Image(); image.onload = () => { let canvas = document.createElement("canvas"); let ctx = canvas.getContext("2d"); let width = image.width; let height = image.height; let quality = 0.9; let dataUrl; do { canvas.width = width; canvas.height = height; ctx?.clearRect(0, 0, canvas.width, canvas.height); ctx?.drawImage(image, 0, 0, width, height); dataUrl = canvas.toDataURL("image/jpeg", quality); if (dataUrl.length < maxSize) break; if (quality > 0.5) { // Prioritize quality reduction quality -= 0.1; } else { // Then reduce the size width *= 0.9; height *= 0.9; } } while (dataUrl.length > maxSize); resolve(dataUrl); }; image.onerror = reject; image.src = readerEvent.target.result; }; reader.onerror = reject; if (file.type.includes("heic")) { try { const heic2any = require("heic2any"); heic2any({ blob: file, toType: "image/jpeg" }) .then((blob: Blob) => { reader.readAsDataURL(blob); }) .catch((e: any) => { reject(e); }); } catch (e) { reject(e); } } reader.readAsDataURL(file); }); } export async function preProcessImageContent( content: RequestMessage["content"], ) { if (typeof content === "string") { return content; } const result = []; for (const part of content) { if (part?.type == "image_url" && part?.image_url?.url) { try { const url = await cacheImageToBase64Image(part?.image_url?.url); result.push({ type: part.type, image_url: { url } }); } catch (error) { console.error("Error processing image URL:", error); } } else { result.push({ ...part }); } } return result; } const imageCaches: Record = {}; export function cacheImageToBase64Image(imageUrl: string) { if (imageUrl.includes(CACHE_URL_PREFIX)) { if (!imageCaches[imageUrl]) { const reader = new FileReader(); return fetch(imageUrl, { method: "GET", mode: "cors", credentials: "include", }) .then((res) => res.blob()) .then( async (blob) => (imageCaches[imageUrl] = await compressImage(blob, 256 * 1024)), ); // compressImage } return Promise.resolve(imageCaches[imageUrl]); } return Promise.resolve(imageUrl); } export function base64Image2Blob(base64Data: string, contentType: string) { const byteCharacters = atob(base64Data); const byteNumbers = new Array(byteCharacters.length); for (let i = 0; i < byteCharacters.length; i++) { byteNumbers[i] = byteCharacters.charCodeAt(i); } const byteArray = new Uint8Array(byteNumbers); return new Blob([byteArray], { type: contentType }); } export function uploadImage(file: File): Promise { if (!window._SW_ENABLED) { // if serviceWorker register error, using compressImage return compressImage(file, 256 * 1024); } const body = new FormData(); body.append("file", file); return fetch(UPLOAD_URL, { method: "post", body, mode: "cors", credentials: "include", }) .then((res) => res.json()) .then((res) => { console.log("res", res); if (res?.code == 0 && res?.data) { return res?.data; } throw Error(`upload Error: ${res?.msg}`); }); } export function removeImage(imageUrl: string) { return fetch(imageUrl, { method: "DELETE", mode: "cors", credentials: "include", }); }