refactor: 根据官方示例重写enhance API,使用异步模式(创建任务+轮询)
This commit is contained in:
parent
94c45bbc4e
commit
05ea65964b
@ -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;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user