diff --git a/CHANGELOG.md b/CHANGELOG.md index ed6e23968..5239f8697 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ ## [Unreleased] ### 修复 +- 本地下载遇到大量同名文件时继续递增编号,避免超过 999 个编号后重复使用进程号文件名并覆盖之前的下载 - 云端协同对端专家的模型、连接器、知识库和对话模式会显示在本端输入框上方 - 侧栏 Beta 标记跟随主题色 - 管理员用量统计里,其他用户创建的专家显示名称而不是 id diff --git a/src/octop/infra/utils/user_downloads.py b/src/octop/infra/utils/user_downloads.py index 9dfba0cb9..382617aab 100644 --- a/src/octop/infra/utils/user_downloads.py +++ b/src/octop/infra/utils/user_downloads.py @@ -63,14 +63,12 @@ def unique_download_path(filename: str, *, home: Path | None = None) -> Path: dest_dir.mkdir(parents=True, exist_ok=True) safe = sanitize_download_filename(filename) path = dest_dir / safe - if not path.exists(): - return path stem, suffix = path.stem, path.suffix - for index in range(1, 1000): - candidate = dest_dir / f"{stem} ({index}){suffix}" - if not candidate.exists(): - return candidate - return dest_dir / f"{stem}-{os.getpid()}{suffix}" + index = 1 + while path.exists(): + path = dest_dir / f"{stem} ({index}){suffix}" + index += 1 + return path def save_user_download( diff --git a/tests/unit/utils/test_user_downloads.py b/tests/unit/utils/test_user_downloads.py index 3d718aaa1..22b69fa1c 100644 --- a/tests/unit/utils/test_user_downloads.py +++ b/tests/unit/utils/test_user_downloads.py @@ -45,6 +45,22 @@ def test_save_user_download_writes_bytes(tmp_path: Path) -> None: assert path.parent == downloads +def test_save_user_download_preserves_files_after_many_collisions(tmp_path: Path) -> None: + downloads = tmp_path / "Downloads" + downloads.mkdir() + (downloads / "note.txt").write_bytes(b"original") + for index in range(1, 1000): + (downloads / f"note ({index}).txt").touch() + + first = save_user_download("note.txt", b"first", home=tmp_path) + second = save_user_download("note.txt", b"second", home=tmp_path) + + assert first != second + assert first.read_bytes() == b"first" + assert second.read_bytes() == b"second" + assert (downloads / "note.txt").read_bytes() == b"original" + + def test_local_dashboard_request_requires_loopback_host_and_peer() -> None: assert is_local_dashboard_request(_Req("127.0.0.1:8088", "127.0.0.1")) assert is_local_dashboard_request(_Req("localhost:8088", "127.0.0.1"))