yikart/AiToEarn · error · Error

获取上传签名失败

Error message

获取上传签名失败

What it means

thrown by getPresignedPostData when the backend response for requesting an OSS/S3 presigned upload is missing or its code !== 0 (the app's success code). It prefers the server-provided res.message and falls back to the generic '获取上传签名失败'. Indicates the upload could not even start because no signed POST data was issued.

Solutions

  1. Log the full res object (code and message) — the server message usually names the exact cause.
  2. Re-authenticate if the backend code indicates 401/invalid token.
  3. Verify contentType/extension maps to a supported asset type in getAssetType.
  4. Check backend presign service config (bucket/keys) if the message points to server-side failure.

Example fix

// before
const res = await request<PresignedData>({ url: '/assets/presign', data })
if (!res || res.code !== 0) throw new Error(res?.message || '获取上传签名失败')
// after
if (!res) throw new Error('网络错误,无法获取上传签名')
if (res.code !== 0) throw new Error(`获取上传签名失败: ${res.message} (code=${res.code})`)
Defensive patterns

Strategy: try-catch

Validate before calling

// before calling presign
if (!file.name || file.size <= 0) throw new Error('文件无效')
if (!isLoggedIn()) await refreshToken()

Type guard

function hasPresignedData(r: unknown): r is { code: number; message?: string; data: PresignedPostData } {
  const o = r as any
  return !!o && typeof o.code === 'number' && !!o.data && typeof o.data.url === 'string'
}

Try / catch

try {
  const presigned = await getPresignedPostData(fileName, fileSize, contentType)
}
catch (e) {
  showToast(e instanceof Error ? e.message : '获取上传签名失败')
  return
}

Prevention

When it happens

Trigger: Calling getPresignedPostData with fileName/fileSize/contentType when the backend fails: auth failure (401 wrapped in res.code), asset type not recognized, backend storage service misconfigured, or request returned undefined (network/HTTP error swallowed by the request layer).

Common situations: Expired login token; backend S3/OSS credentials invalid; getAssetType returning an unsupported type for the file extension; request interceptor returning undefined on network error.

Related errors


AI-assisted analysis of yikart/AiToEarn@d3aa8bea5b (2026-08-31). Data as JSON: /api/errors/5bbf3b90c060e697. Report an issue: GitHub.

Appendix: source

Thrown at project/aitoearn-web/src/api/materials/material.api.ts:79

  return ownerId
    ? `${ownerId}/${hashedPrefix}${processedFileName}`
    : `${hashedPrefix}${processedFileName}`
}

// 获取 R2 presigned post 数据
async function getPresignedPostData(fileName: string, fileSize: number, contentType: string, publicUploadId?: string) {
  const res = await request<UploadSignData>({
    url: getUploadRequestPath(publicUploadId),
    method: 'POST',
    data: {
      filename: fileName,
      size: fileSize,
      type: getAssetType(fileName, contentType),
    },
  })

  if (!res || res.code !== 0)
    throw new Error(res?.message || '获取上传签名失败')

  return res.data
}

async function confirmUpload(assetId: string, fallbackUrl: string, publicUploadId?: string) {
  const confirmResponse = await request<ConfirmUploadData>({
    url: getConfirmRequestPath(assetId, publicUploadId),
    method: 'POST',
    data: {
      id: assetId,
    },
  })

  if (!confirmResponse || confirmResponse.code !== 0)
    throw new Error(confirmResponse?.message || '上传确认失败')

  return confirmResponse.data?.url || fallbackUrl
}

View on GitHub (pinned to d3aa8bea5b)