目录功能扩展: 文件挂目录 (上传 --dir + init dir_id), --list 显示目录+文件归属, --move/--clone
This commit is contained in:
+50
-1
@@ -214,6 +214,21 @@ class TransferClient:
|
|||||||
if not payload.get("ok"):
|
if not payload.get("ok"):
|
||||||
raise TransferError(f"删除目录失败: {payload}")
|
raise TransferError(f"删除目录失败: {payload}")
|
||||||
|
|
||||||
|
def move_file(self, file_id: str, dir_id: int) -> None:
|
||||||
|
"""PUT /api/files/{id}/move: 移动文件到目录 (0=根)"""
|
||||||
|
code, payload = self._request(
|
||||||
|
"PUT", f"/api/files/{file_id}/move", json_body={"dir_id": dir_id})
|
||||||
|
if code != 200:
|
||||||
|
raise TransferError(f"移动失败 (HTTP {code}): {payload}")
|
||||||
|
|
||||||
|
def clone_file(self, file_id: str, dir_id: int) -> dict[str, Any]:
|
||||||
|
"""POST /api/files/{id}/clone: 克隆文件到目录 (0=根)"""
|
||||||
|
code, payload = self._request(
|
||||||
|
"POST", f"/api/files/{file_id}/clone", json_body={"dir_id": dir_id})
|
||||||
|
if code != 200:
|
||||||
|
raise TransferError(f"克隆失败 (HTTP {code}): {payload}")
|
||||||
|
return payload
|
||||||
|
|
||||||
def download(self, file_id: str, dest: Path | None = None) -> tuple[Path, dict[str, Any]]:
|
def download(self, file_id: str, dest: Path | None = None) -> tuple[Path, dict[str, Any]]:
|
||||||
"""逐卷下载密文 (零合并: 服务端只存卷, 客户端本地拼接), 返回 (密文路径, 解密参数)。
|
"""逐卷下载密文 (零合并: 服务端只存卷, 客户端本地拼接), 返回 (密文路径, 解密参数)。
|
||||||
|
|
||||||
@@ -715,6 +730,9 @@ def main() -> int:
|
|||||||
ap.add_argument("--dir-create", metavar="名称", help="创建目录 (按用户隔离)")
|
ap.add_argument("--dir-create", metavar="名称", help="创建目录 (按用户隔离)")
|
||||||
ap.add_argument("--dir-delete", metavar="DIR_ID", help="删除目录")
|
ap.add_argument("--dir-delete", metavar="DIR_ID", help="删除目录")
|
||||||
ap.add_argument("--dir-list", action="store_true", help="列出目录")
|
ap.add_argument("--dir-list", action="store_true", help="列出目录")
|
||||||
|
ap.add_argument("--dir", type=int, metavar="DIR_ID", help="上传到指定目录 (默认根目录)")
|
||||||
|
ap.add_argument("--move", nargs=2, metavar=("文件序号/ID", "DIR_ID|0"), help="移动文件到目录 (0=根)")
|
||||||
|
ap.add_argument("--clone", nargs=2, metavar=("文件序号/ID", "DIR_ID|0"), help="克隆文件到目录 (0=根)")
|
||||||
args = ap.parse_args()
|
args = ap.parse_args()
|
||||||
|
|
||||||
server = args.server or _config_server()
|
server = args.server or _config_server()
|
||||||
@@ -796,16 +814,23 @@ def main() -> int:
|
|||||||
if args.list:
|
if args.list:
|
||||||
try:
|
try:
|
||||||
files = client.list_files()
|
files = client.list_files()
|
||||||
|
dirs = client.list_dirs()
|
||||||
except TransferError as e:
|
except TransferError as e:
|
||||||
print(f"[错误] {e}")
|
print(f"[错误] {e}")
|
||||||
return 1
|
return 1
|
||||||
|
if dirs:
|
||||||
|
print(f"[目录] 共 {len(dirs)} 个:")
|
||||||
|
for d in dirs:
|
||||||
|
cnt = sum(1 for f in files if f.get("dir_id") == d["id"])
|
||||||
|
print(f" {d['id']:>2}. {d['name']} ({cnt} 个文件)")
|
||||||
print(f"[文件] 共 {len(files)} 个:")
|
print(f"[文件] 共 {len(files)} 个:")
|
||||||
for i, f in enumerate(files, 1):
|
for i, f in enumerate(files, 1):
|
||||||
size_mb = f["size"] / 1048576
|
size_mb = f["size"] / 1048576
|
||||||
name = _decrypt_name(f["file_name"]) or f["file_name"]
|
name = _decrypt_name(f["file_name"]) or f["file_name"]
|
||||||
if name.startswith("enc:"):
|
if name.startswith("enc:"):
|
||||||
name = name[:22] + "..." # 加密名截断 (keyring 无对应密钥)
|
name = name[:22] + "..." # 加密名截断 (keyring 无对应密钥)
|
||||||
print(f" {i:>2}. {name} ({size_mb:.1f} MB)")
|
loc = f"[{f.get('dir_name') or '/'}] "
|
||||||
|
print(f" {i:>2}. {loc}{name} ({size_mb:.1f} MB)")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
if args.download:
|
if args.download:
|
||||||
@@ -918,10 +943,34 @@ def main() -> int:
|
|||||||
print(f"[删除] 已删除: {args.delete}")
|
print(f"[删除] 已删除: {args.delete}")
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
if args.move:
|
||||||
|
try:
|
||||||
|
src, dst = args.move
|
||||||
|
fid = _resolve_id(client, src)
|
||||||
|
client.move_file(fid, int(dst))
|
||||||
|
except (TransferError, ValueError) as e:
|
||||||
|
print(f"[错误] {e}")
|
||||||
|
return 1
|
||||||
|
print(f"[移动] {src} -> 目录 {dst}")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
if args.clone:
|
||||||
|
try:
|
||||||
|
src, dst = args.clone
|
||||||
|
fid = _resolve_id(client, src)
|
||||||
|
payload = client.clone_file(fid, int(dst))
|
||||||
|
except (TransferError, ValueError) as e:
|
||||||
|
print(f"[错误] {e}")
|
||||||
|
return 1
|
||||||
|
print(f"[克隆] {src} -> 目录 {dst} (新 file_id={payload['file_id']})")
|
||||||
|
return 0
|
||||||
|
|
||||||
if not (args.init and args.manifest):
|
if not (args.init and args.manifest):
|
||||||
ap.error("需指定 --list / --quota / --download / --delete / (--init + --manifest) 之一")
|
ap.error("需指定 --list / --quota / --download / --delete / (--init + --manifest) 之一")
|
||||||
|
|
||||||
init_json = json.loads(Path(args.init).read_text(encoding="utf-8"))
|
init_json = json.loads(Path(args.init).read_text(encoding="utf-8"))
|
||||||
|
if args.dir is not None:
|
||||||
|
init_json["dir_id"] = args.dir # 上传到指定目录 (服务端校验归属)
|
||||||
manifest = json.loads(Path(args.manifest).read_text(encoding="utf-8"))
|
manifest = json.loads(Path(args.manifest).read_text(encoding="utf-8"))
|
||||||
chunk_files = {
|
chunk_files = {
|
||||||
ch["index"]: Path(ch["filename"]).resolve()
|
ch["index"]: Path(ch["filename"]).resolve()
|
||||||
|
|||||||
Reference in New Issue
Block a user