fix(viewer): 更新下载文件逻辑以处理大文件并返回正确的响应

This commit is contained in:
Wenjie Zhang 2026-03-25 13:39:24 +08:00
parent 1d8a5e88af
commit 637981b060
3 changed files with 39 additions and 6 deletions

View File

@ -117,7 +117,7 @@ DEFAULT_CHAT_MODEL_PROVIDERS: dict[str, ChatModelProvider] = {
),
"minimax": ChatModelProvider(
name="MiniMax",
url="https://platform.minimaxi.com/document/introduction",
url="https://platform.minimaxi.com/docs/guides/models-intro",
base_url="https://api.minimaxi.com/v1",
default="MiniMax-M2.7",
env="MINIMAX_API_KEY",

View File

@ -7,9 +7,9 @@ from pathlib import PurePosixPath
from urllib.parse import quote
from fastapi import HTTPException
from fastapi.responses import StreamingResponse
from fastapi.responses import FileResponse, StreamingResponse
from sqlalchemy.ext.asyncio import AsyncSession
from yuxi.agents.backends.sandbox import SKILLS_PATH, USER_DATA_PATH
from yuxi.agents.backends.sandbox import SKILLS_PATH, USER_DATA_PATH, resolve_virtual_path
from yuxi.agents.backends.skills_backend import SelectedSkillsReadonlyBackend
from yuxi.agents.middlewares.skills_middleware import normalize_selected_skills
from yuxi.services.filesystem_service import _resolve_filesystem_state
@ -199,7 +199,7 @@ async def download_viewer_file(
db: AsyncSession,
) -> StreamingResponse:
normalized_path = _normalize_path(path)
sandbox_backend, skills_backend, _selected_skills = await _resolve_viewer_state(
_sandbox_backend, skills_backend, _selected_skills = await _resolve_viewer_state(
thread_id=thread_id,
agent_id=agent_id,
agent_config_id=agent_config_id,
@ -209,8 +209,20 @@ async def download_viewer_file(
try:
if _is_user_data_path(normalized_path):
responses = await asyncio.to_thread(sandbox_backend.download_files, [normalized_path])
elif _is_skills_path(normalized_path):
actual_path = resolve_virtual_path(thread_id, normalized_path)
if not actual_path.exists():
raise HTTPException(status_code=404, detail="文件不存在")
if not actual_path.is_file():
raise HTTPException(status_code=400, detail="当前路径是目录")
file_name = actual_path.name or "download"
media_type = mimetypes.guess_type(file_name)[0] or "application/octet-stream"
headers = {
"Content-Disposition": f"attachment; filename*=UTF-8''{quote(file_name)}",
}
return FileResponse(path=actual_path, media_type=media_type, headers=headers)
if _is_skills_path(normalized_path):
responses = await asyncio.to_thread(skills_backend.download_files, [_strip_skills_prefix(normalized_path)])
else:
raise HTTPException(

View File

@ -6,6 +6,8 @@ import uuid
import pytest
from yuxi.agents.backends.sandbox import ensure_thread_dirs, sandbox_workspace_dir, virtual_path_for_thread_file
pytestmark = [pytest.mark.asyncio, pytest.mark.integration]
@ -107,3 +109,22 @@ async def test_viewer_download_returns_attachment_response(test_client, standard
assert "attachment;" in content_disposition
assert "download_demo" in content_disposition
assert "download-me" in response.text
async def test_viewer_download_returns_full_file_for_large_user_data_content(test_client, standard_user):
headers = standard_user["headers"]
thread_id = await _create_thread_for_user(test_client, headers)
large_content = "0123456789abcdef" * 4096
ensure_thread_dirs(thread_id)
actual_path = sandbox_workspace_dir(thread_id) / "large_download.txt"
actual_path.write_text(large_content, encoding="utf-8")
file_path = virtual_path_for_thread_file(thread_id, actual_path)
response = await test_client.get(
"/api/viewer/filesystem/download",
params={"thread_id": thread_id, "path": file_path},
headers=headers,
)
assert response.status_code == 200, response.text
assert response.content == large_content.encode("utf-8")