diff --git a/API_CONTACTS.md b/API_CONTACTS.md index 84441ec..3e5dbc9 100644 --- a/API_CONTACTS.md +++ b/API_CONTACTS.md @@ -123,7 +123,7 @@ GET /api/contacts/export?search=产品经理&start_date=2024-01-01&end_date=2024 "code": 200, "msg": "success", "data": { - "download_url": "/media/exports/contacts_export_20240304_153045_a1b2c3d4.xlsx", + "download_url": "http://127.0.0.1:8000/media/exports/contacts_export_20240304_153045_a1b2c3d4.xlsx", "filename": "contacts_export_20240304_153045_a1b2c3d4.xlsx", "total_records": 150, "generated_at": "2024-03-04 15:30:45" @@ -135,7 +135,7 @@ GET /api/contacts/export?search=产品经理&start_date=2024-01-01&end_date=2024 | 字段名 | 类型 | 说明 | |--------|------|------| -| download_url | string | 文件下载链接(相对路径) | +| download_url | string | 文件下载链接(完整 URL) | | filename | string | 生成的文件名 | | total_records | integer | 导出的记录总数 | | generated_at | string | 文件生成时间 | @@ -313,6 +313,5 @@ const handleExport = async () => { - token 格式:`Bearer ` 6. **下载链接**: - - 返回的 `download_url` 是相对路径 - - 前端需要拼接完整的服务器地址(如果跨域) - - 或直接使用相对路径(如果同域) + - 返回的 `download_url` 是可直接访问的完整下载 URL + - 前端可直接 `window.open(download_url)` 或 `a.href = download_url` 下载 diff --git a/server/api/contacts.py b/server/api/contacts.py index 3cc14a5..6e0b041 100644 --- a/server/api/contacts.py +++ b/server/api/contacts.py @@ -27,6 +27,14 @@ from server.models import ContactRecord from server.serializers import ContactRecordSerializer +def _normalize_media_url(media_url: str) -> str: + """Normalize MEDIA_URL so it always ends with '/' and is safe for URL拼接.""" + media_url = (media_url or "/media/").strip() + if media_url.startswith(("http://", "https://")): + return f"{media_url.rstrip('/')}/" + return f"/{media_url.strip('/')}/" + + @api_view(["GET", "POST"]) def contact_list(request): if request.method == "GET": @@ -187,8 +195,10 @@ def contact_export(request): # 保存文件 wb.save(filepath) - # 生成下载链接 - download_url = f"{settings.MEDIA_URL}exports/{filename}" + # 生成下载链接(始终返回可直接访问的完整 URL) + media_url = _normalize_media_url(getattr(settings, "MEDIA_URL", "/media/")) + download_path = f"{media_url}exports/{filename}" + download_url = request.build_absolute_uri(download_path) return api_success({ "download_url": download_url,