From 1a60671255cd7c3a62839a91f4f27b3b6d57bb47 Mon Sep 17 00:00:00 2001 From: 134 Date: Sat, 4 Jul 2026 16:21:46 +0800 Subject: [PATCH] fix: retry image downloads for scale composites --- lib/ai/rmbg/service.ts | 72 +++++++++++++++++++++++++++++++----------- 1 file changed, 54 insertions(+), 18 deletions(-) diff --git a/lib/ai/rmbg/service.ts b/lib/ai/rmbg/service.ts index 0916f9150..65c1cc06b 100644 --- a/lib/ai/rmbg/service.ts +++ b/lib/ai/rmbg/service.ts @@ -63,7 +63,8 @@ const AOS_MAX_RESOLUTION = 4096; const AOS_CREATE_TIMEOUT_MS = 120_000; const AOS_POLL_TIMEOUT_MS = 60_000; const AOS_DOWNLOAD_TIMEOUT_MS = 60_000; -const IMAGE_URL_FETCH_TIMEOUT_MS = 60_000; +const IMAGE_URL_FETCH_TIMEOUT_MS = 90_000; +const IMAGE_URL_FETCH_RETRIES = 2; async function createThumbnail(buffer: Buffer, mimeType: string): Promise { try { @@ -498,27 +499,62 @@ function getModelId(provider: RmbgProvider) { : "rmbg-2.0"; } -export async function fetchImageBufferFromUrl(imageUrl: string): Promise { +function getImageUrlLogMeta(imageUrl: string) { try { - return await withTimeout(async (signal) => { - const imageResponse = await fetch(imageUrl, { signal }); - if (!imageResponse.ok) { - throw new RmbgServiceError( - `无法获取图片: ${imageResponse.status} ${imageResponse.statusText}`, - 400 - ); - } - return Buffer.from(await imageResponse.arrayBuffer()); - }, IMAGE_URL_FETCH_TIMEOUT_MS); - } catch (error) { - if (error instanceof RmbgServiceError) { - throw error; - } - logger.warn("source_image_fetch_failed", error); - throw new RmbgServiceError("无法获取图片,请检查图片链接是否有效", 400); + const parsed = new URL(imageUrl); + return { + host: parsed.host, + pathname: parsed.pathname.slice(0, 96), + }; + } catch { + return { host: "", pathname: "" }; } } +export async function fetchImageBufferFromUrl(imageUrl: string): Promise { + let lastError: unknown = null; + + for (let attempt = 1; attempt <= IMAGE_URL_FETCH_RETRIES + 1; attempt += 1) { + try { + return await withTimeout(async (signal) => { + const imageResponse = await fetch(imageUrl, { + headers: { + Accept: "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8", + "User-Agent": "ai-chatbot-image-fetch/1.0", + }, + signal, + }); + if (!imageResponse.ok) { + throw new RmbgServiceError( + `无法获取图片: ${imageResponse.status} ${imageResponse.statusText}`, + 400 + ); + } + return Buffer.from(await imageResponse.arrayBuffer()); + }, IMAGE_URL_FETCH_TIMEOUT_MS); + } catch (error) { + if (error instanceof RmbgServiceError) { + throw error; + } + lastError = error; + logger.warn("source_image_fetch_failed", error, { + attempt, + maxAttempts: IMAGE_URL_FETCH_RETRIES + 1, + ...getImageUrlLogMeta(imageUrl), + }); + if (attempt <= IMAGE_URL_FETCH_RETRIES) { + await sleep(attempt * 1500); + } + } + } + + throw new RmbgServiceError( + "无法获取图片,请检查图片链接是否有效", + 400, + lastError instanceof Error ? lastError.message : String(lastError || "unknown") + ); +} + export async function executeRmbgJob( params: ExecuteRmbgJobParams ): Promise {