增加上传重试

This commit is contained in:
2026-07-02 15:38:54 +08:00
parent 12070ff4fd
commit dde178f8f7
+50 -22
View File
@@ -66,19 +66,57 @@ func (c *Client) ExchangeCookie(ctx context.Context, cookie string) (*CookieExch
return exchangeCookieWithTLSClient(ctx, client, cookie) return exchangeCookieWithTLSClient(ctx, client, cookie)
} }
// uploadMaxRetries is how many extra in-place attempts a transient upload
// failure (transport error / timeout, 429/451/5xx) gets on a fresh connection
// before the error is surfaced.
const uploadMaxRetries = 5
// UploadImage stores a reference image and returns its blob id. Transient
// failures are retried in place (uploadMaxRetries times); the final error keeps
// its original (non-temporary) classification so the account is not penalized
// for a network blip.
func (c *Client) UploadImage(ctx context.Context, token string, content []byte, contentType, engine string) (string, error) { func (c *Client) UploadImage(ctx context.Context, token string, content []byte, contentType, engine string) (string, error) {
client, err := c.newTLSClient() body, err, retryable := c.uploadImageOnce(ctx, token, content, contentType, engine)
for attempt := 0; err != nil && retryable && attempt < uploadMaxRetries && ctx.Err() == nil; attempt++ {
body, err, retryable = c.uploadImageOnce(ctx, token, content, contentType, engine)
}
if err != nil { if err != nil {
return "", err return "", err
} }
var payload map[string]any
if err := json.Unmarshal(body, &payload); err != nil {
return "", err
}
if images, ok := payload["images"].([]any); ok && len(images) > 0 {
if first, ok := images[0].(map[string]any); ok {
if id := strings.TrimSpace(stringValue(first["id"])); id != "" {
return id, nil
}
}
}
if id := strings.TrimSpace(stringValue(payload["id"])); id != "" {
return id, nil
}
return "", errors.New("adobe upload missing blob id")
}
// uploadImageOnce performs a single upload attempt and returns the raw response
// body plus whether a failure is retryable (transport error / 429/451/5xx).
// Auth failures (401/403) and other non-200s are not retryable.
func (c *Client) uploadImageOnce(ctx context.Context, token string, content []byte, contentType, engine string) ([]byte, error, bool) {
client, err := c.newTLSClient()
if err != nil {
return nil, err, false
}
endpoint := uploadURL endpoint := uploadURL
if engine == "firefly-video" { if engine == "firefly-video" {
endpoint = fireflyVideoUploadURL endpoint = fireflyVideoUploadURL
} }
req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewReader(content)) req, err := http.NewRequest(http.MethodPost, endpoint, bytes.NewReader(content))
if err != nil { if err != nil {
return "", err return nil, err, false
} }
req = req.WithContext(ctx) req = req.WithContext(ctx)
req.Header = http.Header{ req.Header = http.Header{
@@ -98,36 +136,26 @@ func (c *Client) UploadImage(ctx context.Context, token string, content []byte,
resp, err := client.Do(req) resp, err := client.Do(req)
if err != nil { if err != nil {
return "", fmt.Errorf("adobe upload request: %w", err) // Transport error (incl. Client.Timeout on the storage endpoint) — a
// network blip, retryable on a fresh connection.
return nil, fmt.Errorf("adobe upload request: %w", err), true
} }
defer resp.Body.Close() defer resp.Body.Close()
body, err := io.ReadAll(resp.Body) body, err := io.ReadAll(resp.Body)
if err != nil { if err != nil {
return "", err return nil, err, true
} }
if resp.StatusCode == 401 || resp.StatusCode == 403 { if resp.StatusCode == 401 || resp.StatusCode == 403 {
return "", fmt.Errorf("%w (upload %d %s: %s)", ErrAuth, resp.StatusCode, resp.Header.Get("x-access-error"), clip(body, 300)) return nil, fmt.Errorf("%w (upload %d %s: %s)", ErrAuth, resp.StatusCode, resp.Header.Get("x-access-error"), clip(body, 300)), false
}
if resp.StatusCode == 429 || resp.StatusCode == 451 || resp.StatusCode >= 500 {
return nil, fmt.Errorf("adobe upload failed: %d %s", resp.StatusCode, clip(body, 300)), true
} }
if resp.StatusCode != 200 { if resp.StatusCode != 200 {
return "", fmt.Errorf("adobe upload failed: %d %s", resp.StatusCode, clip(body, 300)) return nil, fmt.Errorf("adobe upload failed: %d %s", resp.StatusCode, clip(body, 300)), false
} }
return body, nil, true
var payload map[string]any
if err := json.Unmarshal(body, &payload); err != nil {
return "", err
}
if images, ok := payload["images"].([]any); ok && len(images) > 0 {
if first, ok := images[0].(map[string]any); ok {
if id := strings.TrimSpace(stringValue(first["id"])); id != "" {
return id, nil
}
}
}
if id := strings.TrimSpace(stringValue(payload["id"])); id != "" {
return id, nil
}
return "", errors.New("adobe upload missing blob id")
} }
func (c *Client) GenerateImage(ctx context.Context, token, modelID, prompt, aspectRatio, resolution string, blobIDs []string) ([]byte, map[string]any, error) { func (c *Client) GenerateImage(ctx context.Context, token, modelID, prompt, aspectRatio, resolution string, blobIDs []string) ([]byte, map[string]any, error) {