|
|
@@ -263,35 +263,54 @@ async def import_project_dataset(
|
|
|
download_token = file_url.rstrip("/").split("/")[-1]
|
|
|
|
|
|
# 3. 通过独立的下载接口获取文件(文档 4.6 节)
|
|
|
+ # 标注平台导出是异步的,文件可能还没生成,需要轮询
|
|
|
await get_token()
|
|
|
base_url = _get_base_url()
|
|
|
download_url = f"{base_url}/api/v1/open/datasets/downloads/{download_token}"
|
|
|
|
|
|
- async with httpx.AsyncClient(timeout=120) as client:
|
|
|
- # 先手动处理重定向,确保每次请求都带上认证头
|
|
|
- resp = await client.get(
|
|
|
- download_url,
|
|
|
- headers=_auth_headers(),
|
|
|
- follow_redirects=False,
|
|
|
- )
|
|
|
- # 手动跟随重定向,每次都带上认证头
|
|
|
- redirect_count = 0
|
|
|
- while resp.is_redirect and redirect_count < 5:
|
|
|
- redirect_url = resp.next_request.url
|
|
|
- logger.info(f"Download redirect to: {redirect_url}")
|
|
|
+ file_content = b""
|
|
|
+ max_retries = 6
|
|
|
+ for attempt in range(max_retries):
|
|
|
+ async with httpx.AsyncClient(timeout=120) as client:
|
|
|
resp = await client.get(
|
|
|
- str(redirect_url),
|
|
|
+ download_url,
|
|
|
headers=_auth_headers(),
|
|
|
follow_redirects=False,
|
|
|
)
|
|
|
- redirect_count += 1
|
|
|
- resp.raise_for_status()
|
|
|
- file_content = resp.content
|
|
|
+ # 手动跟随重定向,每次都带上认证头
|
|
|
+ redirect_count = 0
|
|
|
+ while resp.is_redirect and redirect_count < 5:
|
|
|
+ redirect_url = resp.next_request.url
|
|
|
+ logger.info(f"Download redirect to: {redirect_url}")
|
|
|
+ resp = await client.get(
|
|
|
+ str(redirect_url),
|
|
|
+ headers=_auth_headers(),
|
|
|
+ follow_redirects=False,
|
|
|
+ )
|
|
|
+ redirect_count += 1
|
|
|
+ resp.raise_for_status()
|
|
|
+ file_content = resp.content
|
|
|
+
|
|
|
+ # 文件内容足够大,说明下载成功
|
|
|
+ if len(file_content) > 10:
|
|
|
+ break
|
|
|
+
|
|
|
+ # 内容为空或太小(如 [] 或 {}),文件可能还没生成
|
|
|
+ if attempt < max_retries - 1:
|
|
|
+ wait = 2 ** attempt # 1, 2, 4, 8, 16 秒
|
|
|
+ logger.info(
|
|
|
+ f"Download attempt {attempt + 1}/{max_retries}: "
|
|
|
+ f"file too small ({len(file_content)} bytes), retrying in {wait}s..."
|
|
|
+ )
|
|
|
+ import asyncio
|
|
|
+ await asyncio.sleep(wait)
|
|
|
+ else:
|
|
|
+ logger.warning(f"Download failed after {max_retries} attempts, file still empty")
|
|
|
|
|
|
logger.info(
|
|
|
f"Downloaded annotation file: {len(file_content)} bytes, "
|
|
|
f"content_type={resp.headers.get('content-type', 'unknown')}, "
|
|
|
- f"url={resp.url}, redirects={redirect_count}"
|
|
|
+ f"redirects={redirect_count}"
|
|
|
)
|
|
|
if len(file_content) < 200:
|
|
|
logger.warning(f"Annotation file content suspiciously small: {file_content!r}")
|