From 27f357b1d2fb72d73c69ee99f4c4d3431ecbff1a Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sat, 29 Aug 2026 10:34:14 +0800 Subject: [PATCH] fix(transfer): create local target before remote download (#6493) --- app/modules/filemanager/transhandler.py | 7 ++-- tests/test_transfer_execution_runner.py | 43 +++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 4 deletions(-) diff --git a/app/modules/filemanager/transhandler.py b/app/modules/filemanager/transhandler.py index 579c13fc6..762c46503 100644 --- a/app/modules/filemanager/transhandler.py +++ b/app/modules/filemanager/transhandler.py @@ -1410,7 +1410,7 @@ class TransHandler: transfer_type: str, ) -> Tuple[Optional[FileItem], str]: """ - 处理单个文件 + 处理单个文件,确保跨存储下载的本地目标目录已准备就绪 :param fileitem: 源文件 :param target_storage: 目标存储 :param source_oper: 源存储操作对象 @@ -1538,14 +1538,13 @@ class TransHandler: return __get_targetitem(target_file), "" # 网盘到本地 if transfer_type in ["copy", "move"]: + # 远程存储适配器会直接在传入目录创建文件,下载前必须先建好目录。 + target_file.parent.mkdir(parents=True, exist_ok=True) # 下载 tmp_file = source_oper.download( fileitem=fileitem, path=target_file.parent ) if tmp_file: - # 创建目录 - if not target_file.parent.exists(): - target_file.parent.mkdir(parents=True, exist_ok=True) # 将tmp_file移动后target_file SystemUtils.move(tmp_file, target_file) if transfer_type == "move": diff --git a/tests/test_transfer_execution_runner.py b/tests/test_transfer_execution_runner.py index ee7b124e5..d3b8a4855 100644 --- a/tests/test_transfer_execution_runner.py +++ b/tests/test_transfer_execution_runner.py @@ -401,3 +401,46 @@ def test_cross_storage_move_materializes_before_independent_source_delete(tmp_pa assert runner.steps[0][2]["transfer_type"] == "copy" target_oper.upload.assert_called_once() source_oper.delete.assert_called_once_with(source_item) + + +def test_remote_to_local_transfer_creates_target_directory_before_download(tmp_path): + """网盘到本地的下载开始前必须先创建缺失的目标目录。""" + target_file = tmp_path / "library" / "Season 1" / "episode.mkv" + source_item = FileItem( + storage="alist", + path="/downloads/episode.mkv", + name="episode.mkv", + type="file", + size=5, + extension="mkv", + ) + + def download(*, fileitem, path): + """模拟只接受已存在本地目录的网盘下载适配器。""" + assert fileitem is source_item + assert path == target_file.parent + assert path.is_dir() + temporary_file = path / fileitem.name + temporary_file.write_bytes(b"movie") + return temporary_file + + source_oper = Mock() + source_oper.download.side_effect = download + + result, error = TransHandler._TransHandler__transfer_command( + fileitem=source_item, + target_storage="local", + source_oper=source_oper, + target_oper=Mock(), + target_file=target_file, + transfer_type="move", + ) + + assert error == "" + assert result is not None + assert target_file.read_bytes() == b"movie" + source_oper.download.assert_called_once_with( + fileitem=source_item, + path=target_file.parent, + ) + source_oper.delete.assert_called_once_with(source_item)