refactor: 根据官方示例重写enhance API,使用异步模式(创建任务+轮询)
Some checks failed
Lint / build (20) (push) Has been cancelled
Playwright Tests / test (push) Has been cancelled

This commit is contained in:
Your Name 2025-12-29 01:21:48 +08:00
parent 94c45bbc4e
commit 05ea65964b

View File

@ -82,51 +82,68 @@ async function uploadBase64ToS3(base64Data: string, mimeType: string = "image/jp
}
/**
* 轮询获取任务结果
* 轮询获取任务结果(根据官方示例)
*/
async function pollTaskResult(apiUrl: string, apiKey: string, taskId: string, maxAttempts: number = 30): Promise<any> {
async function pollTaskResult(
apiUrl: string,
apiKey: string,
taskId: string,
delay: number = 1000,
timeout: number = 30000
): Promise<any> {
const pollUrl = `${apiUrl}/api/tasks/visual/scale/${taskId}`;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
const response = await fetch(pollUrl, {
method: "GET",
headers: {
"X-API-KEY": apiKey,
},
});
const getTaskResult = async (): Promise<any> => {
const response = await fetch(pollUrl, {
method: "GET",
headers: {
"X-API-KEY": apiKey,
},
});
if (!response.ok) {
throw new Error(`轮询请求失败: ${response.status} ${response.statusText}`);
}
const result = await response.json();
// 检查任务状态
if (result.status === 200 && result.data) {
if (result.data.state === 1) {
// 任务完成
return result.data;
} else if (result.data.state === 0) {
// 任务进行中,继续轮询
await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒
continue;
} else {
// 任务失败
throw new Error(`任务失败: state=${result.data.state}`);
}
} else {
throw new Error(`API返回错误: ${result.message || "未知错误"}`);
}
} catch (error) {
if (attempt === maxAttempts - 1) {
throw error;
}
await new Promise(resolve => setTimeout(resolve, 1000)); // 等待1秒后重试
if (!response.ok) {
throw new Error(`轮询请求失败: ${response.status} ${response.statusText}`);
}
}
const result = await response.json();
if (!result.data) {
throw result;
}
const { progress, state } = result.data;
// state < 0 表示失败
if (state < 0) {
throw result;
}
// progress >= 100 表示完成
if (progress >= 100) {
return result;
}
// 否则继续轮询
throw null;
};
throw new Error("轮询超时,任务未在30秒内完成");
try {
const result = await getTaskResult();
return result;
} catch (error) {
if (error && typeof error === 'object' && 'data' in error) {
throw new Error(JSON.stringify(error, null, 2));
}
if (timeout <= 0) {
throw new Error('轮询超时');
}
await new Promise((resolve) => {
setTimeout(() => {
resolve(undefined);
}, delay);
});
return pollTaskResult(apiUrl, apiKey, taskId, delay, timeout - delay);
}
}
/**
@ -218,7 +235,7 @@ export async function POST(request: NextRequest) {
);
}
// 3. 调用变清晰API(使用同步模式)
// 3. 调用变清晰API(使用异步模式,根据官方示例)
const apiUrl = config.apiUrl.endsWith("/")
? config.apiUrl.slice(0, -1)
: config.apiUrl;
@ -235,7 +252,8 @@ export async function POST(request: NextRequest) {
filename: "image.jpg",
contentType: "image/jpeg",
});
formData.append("sync", "1"); // 同步模式
// 使用异步模式(sync=0),根据官方示例
formData.append("sync", "0");
formData.append("type", config.type || "clean"); // 默认通用清晰化
const scaleFactorValue = typeof config.scaleFactor === "number" ? config.scaleFactor : (config.scaleFactor === "auto" ? undefined : Number.parseInt(String(config.scaleFactor)));
if (scaleFactorValue) {
@ -249,12 +267,12 @@ export async function POST(request: NextRequest) {
};
const startTime = Date.now();
let response: Response;
let taskId: string;
try {
// 添加超时控制(60秒,因为同步模式可能需要更长时间)
// 添加超时控制(30秒)
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 60000);
const timeoutId = setTimeout(() => controller.abort(), 30000);
// 将FormData转换为Buffer(form-data包是stream)
const formDataBuffer = await new Promise<Buffer>((resolve, reject) => {
@ -267,7 +285,7 @@ export async function POST(request: NextRequest) {
// 将Buffer转换为Uint8Array用于fetch(更兼容)
const uint8Array = new Uint8Array(formDataBuffer);
response = await fetch(enhanceUrl, {
const createResponse = await fetch(enhanceUrl, {
method: "POST",
headers,
body: uint8Array,
@ -275,6 +293,20 @@ export async function POST(request: NextRequest) {
});
clearTimeout(timeoutId);
if (!createResponse.ok) {
const errorText = await createResponse.text();
throw new Error(`创建任务失败: ${errorText || createResponse.statusText}`);
}
const createResult = await createResponse.json();
if (!createResult.data || !createResult.data.task_id) {
throw new Error(`API未返回task_id: ${JSON.stringify(createResult)}`);
}
taskId = createResult.data.task_id;
console.log(`[Enhance] 任务已创建,task_id: ${taskId}`);
} catch (fetchError) {
const processingTime = (Date.now() - startTime) / 1000;
console.error("[Enhance API] 网络请求失败:", {
@ -332,50 +364,34 @@ export async function POST(request: NextRequest) {
);
}
const processingTime = (Date.now() - startTime) / 1000;
if (!response.ok) {
const errorText = await response.text();
console.error("[Enhance API] 请求失败:", {
status: response.status,
statusText: response.statusText,
error: errorText,
enhanceUrl,
});
// 4. 轮询获取任务结果(根据官方示例)
console.log(`[Enhance] 开始轮询任务结果,task_id: ${taskId}`);
const taskResult = await pollTaskResult(apiUrl, config.apiKey, taskId);
return NextResponse.json(
{
error: "变清晰API请求失败",
detail: errorText || response.statusText,
},
{ status: response.status }
);
}
const result = await response.json();
// 4. 处理返回结果
if (result.status !== 200 || !result.data) {
return NextResponse.json(
{ error: "变清晰处理失败", detail: result.message || "未知错误" },
{ status: 500 }
);
}
// 检查是否是异步任务(返回task_id)
let enhancedImageBase64: string;
let finalProcessingTime = processingTime;
if (result.data.task_id && !result.data.image && !result.data.image_base64) {
// 异步模式,需要轮询
console.log(`[Enhance] 异步任务,task_id: ${result.data.task_id}`);
const taskResult = await pollTaskResult(apiUrl, config.apiKey, result.data.task_id);
const finalProcessingTime = (Date.now() - startTime) / 1000;
if (taskResult.image_base64) {
enhancedImageBase64 = taskResult.image_base64;
} else if (taskResult.image) {
// 5. 处理返回结果
if (taskResult.status !== 200 || !taskResult.data) {
return NextResponse.json(
{ error: "变清晰处理失败", detail: taskResult.message || "未知错误" },
{ status: 500 }
);
}
let enhancedImageBase64: string;
if (taskResult.data.image_base64) {
// 返回的是base64
enhancedImageBase64 = taskResult.data.image_base64;
} else if (taskResult.data.image) {
// 返回的是URL,需要下载
const imageResponse = await fetch(taskResult.image);
const imageResponse = await fetch(taskResult.data.image);
if (!imageResponse.ok) {
return NextResponse.json(
{ error: "变清晰处理失败", detail: "无法下载处理后的图片" },
{ status: 500 }
);
}
const imageBuffer = await imageResponse.arrayBuffer();
enhancedImageBase64 = Buffer.from(imageBuffer).toString("base64");
} else {
@ -384,34 +400,13 @@ export async function POST(request: NextRequest) {
{ status: 500 }
);
}
finalProcessingTime = (Date.now() - startTime) / 1000;
} else if (result.data.image_base64) {
// 同步模式,直接返回base64
enhancedImageBase64 = result.data.image_base64;
} else if (result.data.image) {
// 返回的是URL,需要下载
const imageResponse = await fetch(result.data.image);
if (!imageResponse.ok) {
if (!enhancedImageBase64) {
return NextResponse.json(
{ error: "变清晰处理失败", detail: "无法下载处理后的图片" },
{ error: "变清晰处理失败", detail: "无法获取处理后的图片" },
{ status: 500 }
);
}
const imageBuffer = await imageResponse.arrayBuffer();
enhancedImageBase64 = Buffer.from(imageBuffer).toString("base64");
} else {
return NextResponse.json(
{ error: "变清晰处理失败", detail: "API未返回处理后的图片" },
{ status: 500 }
);
}
if (!enhancedImageBase64) {
return NextResponse.json(
{ error: "变清晰处理失败", detail: "无法获取处理后的图片" },
{ status: 500 }
);
}
// 5. 上传处理后的图片到S3存储桶
let thumbnailUrl: string;