Files
Genarrative/src/services/image-editor/editorRetryOptions.ts
T
kdletters 9ad2152816 为图片编辑器上传和生成加自动重试
新增图片编辑器请求重试配置

生成请求遇到429等临时错误自动重试

上传凭证、资产确认和直传上传共用重试策略

补充图片编辑器重试参数和直传429测试
2026-06-23 16:30:54 +08:00

77 lines
1.9 KiB
TypeScript

import type { ApiRetryOptions } from '../apiClient';
const EDITOR_RETRYABLE_STATUS_CODES = [408, 425, 429, 502, 503, 504];
const EDITOR_RETRY_MAX_RETRIES = 2;
const EDITOR_RETRY_BASE_DELAY_MS = 800;
const EDITOR_RETRY_MAX_DELAY_MS = 3000;
export const EDITOR_REQUEST_RETRY_OPTIONS = {
maxRetries: EDITOR_RETRY_MAX_RETRIES,
baseDelayMs: EDITOR_RETRY_BASE_DELAY_MS,
maxDelayMs: EDITOR_RETRY_MAX_DELAY_MS,
retryUnsafeMethods: true,
retryableStatusCodes: EDITOR_RETRYABLE_STATUS_CODES,
} satisfies ApiRetryOptions;
type EditorDirectUploadTarget = {
host: string;
formFields: Record<string, string | null | undefined>;
};
function buildRetryDelayMs(attempt: number) {
return Math.min(
EDITOR_RETRY_MAX_DELAY_MS,
EDITOR_RETRY_BASE_DELAY_MS * Math.max(1, attempt),
);
}
function waitForRetryDelay(attempt: number) {
return new Promise((resolve) => {
setTimeout(resolve, buildRetryDelayMs(attempt));
});
}
function isRetryableEditorUploadStatus(status: number) {
return EDITOR_RETRYABLE_STATUS_CODES.includes(status);
}
function buildDirectUploadFormData(
upload: EditorDirectUploadTarget,
file: File,
) {
const formData = new FormData();
Object.entries(upload.formFields).forEach(([key, value]) => {
if (value !== null && value !== undefined) {
formData.append(key, value);
}
});
formData.append('file', file, file.name);
return formData;
}
export async function postEditorDirectUploadFile(
upload: EditorDirectUploadTarget,
file: File,
errorMessage: string,
) {
for (let attempt = 0; ; attempt += 1) {
const response = await fetch(upload.host, {
method: 'POST',
body: buildDirectUploadFormData(upload, file),
});
if (response.ok) {
return;
}
if (
attempt >= EDITOR_RETRY_MAX_RETRIES ||
!isRetryableEditorUploadStatus(response.status)
) {
throw new Error(errorMessage);
}
await waitForRetryDelay(attempt + 1);
}
}