fix storage

This commit is contained in:
jxxghp
2024-06-30 18:44:23 +08:00
parent a62ca9a226
commit a7b906ada6
9 changed files with 164 additions and 114 deletions
-1
View File
@@ -9,7 +9,6 @@ from app.chain.transfer import TransferChain
from app.core.config import settings from app.core.config import settings
from app.core.metainfo import MetaInfoPath from app.core.metainfo import MetaInfoPath
from app.core.security import verify_token, verify_uri_token from app.core.security import verify_token, verify_uri_token
from app.modules.filetransfer.storage.alipan import AliyunHelper
from app.helper.progress import ProgressHelper from app.helper.progress import ProgressHelper
from app.schemas.types import ProgressKey from app.schemas.types import ProgressKey
-1
View File
@@ -10,7 +10,6 @@ from app.core.config import settings
from app.core.metainfo import MetaInfoPath from app.core.metainfo import MetaInfoPath
from app.core.security import verify_token, verify_uri_token from app.core.security import verify_token, verify_uri_token
from app.helper.progress import ProgressHelper from app.helper.progress import ProgressHelper
from app.modules.filetransfer.storage.u115 import U115Helper
from app.schemas.types import ProgressKey from app.schemas.types import ProgressKey
from app.utils.http import RequestUtils from app.utils.http import RequestUtils
+44 -13
View File
@@ -181,7 +181,7 @@ class FileTransferModule(_ModuleBase):
pass pass
def __transfer_command(self, fileitem: FileItem, target_storage: str, def __transfer_command(self, fileitem: FileItem, target_storage: str,
target_file: Path, transfer_type: str) -> int: target_file: Path, transfer_type: str) -> bool:
""" """
使用系统命令处理单个文件 使用系统命令处理单个文件
:param fileitem: 源文件 :param fileitem: 源文件
@@ -192,29 +192,60 @@ class FileTransferModule(_ModuleBase):
if fileitem.storage != "local" and target_storage != "local": if fileitem.storage != "local" and target_storage != "local":
logger.error(f"不支持 {fileitem.storage}{target_storage} 的文件整理") logger.error(f"不支持 {fileitem.storage}{target_storage} 的文件整理")
return 1 return False
retcode = 0
# 源操作对象 # 源操作对象
source_oper = self.__get_storage_oper(fileitem.storage) source_oper = self.__get_storage_oper(fileitem.storage)
# 目的操作对象 # 目的操作对象
target_oper = self.__get_storage_oper(target_storage) target_oper = self.__get_storage_oper(target_storage)
with lock: with lock:
if fileitem.storage == "local" and target_storage == "local": if fileitem.storage == "local" and target_storage == "local":
# 本地到本地 # 本地到本地
if transfer_type == "copy": if transfer_type == "copy":
retcode = source_oper.copy(fileitem, target_file) return source_oper.copy(fileitem, target_file)
elif transfer_type == "move": elif transfer_type == "move":
retcode = source_oper.move(fileitem, target_file) return source_oper.move(fileitem, target_file)
elif transfer_type == "link": elif transfer_type == "link":
retcode = source_oper.link(fileitem, target_file) return source_oper.link(fileitem, target_file)
elif transfer_type == "softlink": elif transfer_type == "softlink":
retcode = source_oper.softlink(fileitem, target_file) return source_oper.softlink(fileitem, target_file)
# TODO 本地到网盘 elif fileitem.storage == "local" and target_storage != "local":
# 本地到网盘
# TODO 网盘到本地 if transfer_type == "copy":
# 复制
return retcode filepath = Path(fileitem.path)
if not filepath.exists():
logger.error(f"文件 {filepath} 不存在")
return False
# TODO 根据目的路径创建文件夹
target_fileitem = target_oper.get_folder(target_file.parent)
if target_fileitem:
# 上传文件
return target_oper.upload(target_fileitem, filepath)
elif transfer_type == "move":
# 移动
filepath = Path(fileitem.path)
if not filepath.exists():
logger.error(f"文件 {filepath} 不存在")
return False
# TODO 根据目的路径获取文件夹
target_fileitem = target_oper.get_folder(target_file.parent)
if target_fileitem:
# 上传文件
result = target_oper.upload(target_fileitem, filepath)
if result:
# 删除源文件
return source_oper.delete(fileitem)
elif fileitem.storage != "local" and target_storage == "local":
# 网盘到本地
if transfer_type == "copy":
# 下载
return target_oper.download(fileitem, target_file)
elif transfer_type == "move":
# 下载
if target_oper.download(fileitem, target_file):
# 删除源文件
return source_oper.delete(fileitem)
return False
def __transfer_other_files(self, fileitem: FileItem, target_storage: str, target_file: Path, def __transfer_other_files(self, fileitem: FileItem, target_storage: str, target_file: Path,
transfer_type: str) -> int: transfer_type: str) -> int:
+9 -2
View File
@@ -45,6 +45,13 @@ class StorageBase(metaclass=ABCMeta):
""" """
pass pass
@abstractmethod
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
获取目录
"""
pass
@abstractmethod @abstractmethod
def delete(self, fileitm: schemas.FileItem) -> bool: def delete(self, fileitm: schemas.FileItem) -> bool:
""" """
@@ -60,9 +67,9 @@ class StorageBase(metaclass=ABCMeta):
pass pass
@abstractmethod @abstractmethod
def download(self, fileitm: schemas.FileItem) -> Any: def download(self, fileitm: schemas.FileItem, path: Path):
""" """
下载链接 下载文件,保存到本地
""" """
pass pass
+28 -5
View File
@@ -452,6 +452,12 @@ class AliPan(StorageBase):
self.__handle_error(res, "创建目录") self.__handle_error(res, "创建目录")
return None return None
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
TODO 获取目录,不存在则创建
"""
pass
def delete(self, fileitem: schemas.FileItem) -> bool: def delete(self, fileitem: schemas.FileItem) -> bool:
""" """
删除文件 删除文件
@@ -520,27 +526,35 @@ class AliPan(StorageBase):
self.__handle_error(res, "重命名文件") self.__handle_error(res, "重命名文件")
return False return False
def download(self, fileitem: schemas.FileItem) -> Optional[str]: def download(self, fileitem: schemas.FileItem, path: Path) -> bool:
""" """
获取下载链接 下载文件,保存到本地
""" """
params = self.__access_params params = self.__access_params
if not params: if not params:
return None return False
headers = self.__get_headers(params) headers = self.__get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.download_url, json={ res = RequestUtils(headers=headers, timeout=10).post_res(self.download_url, json={
"drive_id": fileitem.drive_id, "drive_id": fileitem.drive_id,
"file_id": fileitem.fileid "file_id": fileitem.fileid
}) })
if res: if res:
return res.json().get("url") download_url = res.json().get("url")
if not download_url:
return False
res = RequestUtils().get_res(download_url)
if res:
with path.open("wb") as f:
f.write(res.content)
return True
else: else:
self.__handle_error(res, "获取下载链接") self.__handle_error(res, "获取下载链接")
return None return False
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]: def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
""" """
上传文件,并标记完成 上传文件,并标记完成
TODO 上传文件分片、秒传
""" """
params = self.__access_params params = self.__access_params
if not params: if not params:
@@ -634,10 +648,19 @@ class AliPan(StorageBase):
return False return False
def copy(self, fileitm: schemas.FileItem, target_file: Path) -> bool: def copy(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
"""
复制文件
"""
pass pass
def link(self, fileitm: schemas.FileItem, target_file: Path) -> bool: def link(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
"""
硬链接文件
"""
pass pass
def softlink(self, fileitm: schemas.FileItem, target_file: schemas.FileItem) -> bool: def softlink(self, fileitm: schemas.FileItem, target_file: schemas.FileItem) -> bool:
"""
软链接文件
"""
pass pass
+9 -33
View File
@@ -2,8 +2,6 @@ import shutil
from pathlib import Path from pathlib import Path
from typing import Optional, List from typing import Optional, List
from starlette.responses import FileResponse, Response
from app import schemas from app import schemas
from app.log import logger from app.log import logger
from app.modules.filetransfer.storage import StorageBase from app.modules.filetransfer.storage import StorageBase
@@ -117,6 +115,12 @@ class LocalStorage(StorageBase):
modify_time=path_obj.stat().st_mtime, modify_time=path_obj.stat().st_mtime,
) )
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
获取目录
"""
pass
def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]: def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]:
""" """
获取文件详情 获取文件详情
@@ -156,45 +160,17 @@ class LocalStorage(StorageBase):
return False return False
path_obj.rename(path_obj.parent / name) path_obj.rename(path_obj.parent / name)
def download(self, fileitem: schemas.FileItem) -> Optional[Response]: def download(self, fileitem: schemas.FileItem, path: Path) -> bool:
""" """
下载文件 下载文件
""" """
if not fileitem.path: return False
return None
path_obj = Path(fileitem.path)
if not path_obj.exists():
return None
if path_obj.is_file():
# 做为文件流式下载
return FileResponse(path_obj)
else:
# 做为压缩包下载
shutil.make_archive(base_name=path_obj.stem, format="zip", root_dir=path_obj)
reponse = Response(content=path_obj.read_bytes(), media_type="application/zip")
# 删除压缩包
Path(f"{path_obj.stem}.zip").unlink()
return reponse
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]: def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
""" """
上传文件 上传文件
""" """
if not fileitem.path: return None
return None
path_obj = Path(fileitem.path)
if not path_obj.exists():
return None
shutil.copy(path, path_obj / path.name)
return schemas.FileItem(
type="file",
path=str(path_obj / path.name).replace("\\", "/"),
name=path.name,
basename=path.stem,
extension=path.suffix[1:],
size=path.stat().st_size,
modify_time=path.stat().st_mtime,
)
def copy(self, fileitem: schemas.FileItem, target_file: Path) -> bool: def copy(self, fileitem: schemas.FileItem, target_file: Path) -> bool:
""" """
+56 -5
View File
@@ -1,9 +1,12 @@
import subprocess
from pathlib import Path from pathlib import Path
from typing import Optional, Any, List from typing import Optional, List
from app import schemas from app import schemas
from app.log import logger
from app.modules.filetransfer.storage import StorageBase from app.modules.filetransfer.storage import StorageBase
from app.schemas.types import StorageSchema from app.schemas.types import StorageSchema
from app.utils.system import SystemUtils
class Rclone(StorageBase): class Rclone(StorageBase):
@@ -19,6 +22,16 @@ class Rclone(StorageBase):
"copy": "复制" "copy": "复制"
} }
@staticmethod
def __get_hidden_shell():
if SystemUtils.is_windows():
st = subprocess.STARTUPINFO()
st.dwFlags = subprocess.STARTF_USESHOWWINDOW
st.wShowWindow = subprocess.SW_HIDE
return st
else:
return None
def check(self) -> bool: def check(self) -> bool:
pass pass
@@ -28,13 +41,19 @@ class Rclone(StorageBase):
def create_folder(self, fileitm: schemas.FileItem, name: str) -> Optional[schemas.FileItem]: def create_folder(self, fileitm: schemas.FileItem, name: str) -> Optional[schemas.FileItem]:
pass pass
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
获取目录
"""
pass
def delete(self, fileitm: schemas.FileItem) -> bool: def delete(self, fileitm: schemas.FileItem) -> bool:
pass pass
def rename(self, fileitm: schemas.FileItem, name: str) -> bool: def rename(self, fileitm: schemas.FileItem, name: str) -> bool:
pass pass
def download(self, fileitm: schemas.FileItem) -> Any: def download(self, fileitm: schemas.FileItem, path: Path) -> bool:
pass pass
def upload(self, fileitm: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]: def upload(self, fileitm: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
@@ -43,11 +62,43 @@ class Rclone(StorageBase):
def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]: def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]:
pass pass
def move(self, fileitm: schemas.FileItem, target_dir: schemas.FileItem) -> bool: def move(self, fileitm: schemas.FileItem, target_file: schemas.FileItem) -> bool:
pass """
移动文件
"""
try:
retcode = subprocess.run(
[
'rclone', 'moveto',
fileitm.path,
f'MP:{target_file}'
],
startupinfo=self.__get_hidden_shell()
).returncode
if retcode == 0:
return True
except Exception as err:
logger.error(f"移动文件失败:{err}")
return False
def copy(self, fileitm: schemas.FileItem, target_file: Path) -> bool: def copy(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
pass """
复制文件
"""
try:
retcode = subprocess.run(
[
'rclone', 'copyto',
fileitm.path,
f'MP:{target_file}'
],
startupinfo=self.__get_hidden_shell()
).returncode
if retcode == 0:
return True
except Exception as err:
logger.error(f"复制文件失败:{err}")
return False
def link(self, fileitm: schemas.FileItem, target_file: Path) -> bool: def link(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
pass pass
+18 -5
View File
@@ -5,13 +5,14 @@ from typing import Optional, Tuple, List
import oss2 import oss2
import py115 import py115
from py115 import Cloud from py115 import Cloud
from py115.types import LoginTarget, QrcodeSession, QrcodeStatus, Credential, DownloadTicket from py115.types import LoginTarget, QrcodeSession, QrcodeStatus, Credential
from app import schemas from app import schemas
from app.db.systemconfig_oper import SystemConfigOper from app.db.systemconfig_oper import SystemConfigOper
from app.log import logger from app.log import logger
from app.modules.filetransfer.storage import StorageBase from app.modules.filetransfer.storage import StorageBase
from app.schemas.types import SystemConfigKey, StorageSchema from app.schemas.types import SystemConfigKey, StorageSchema
from app.utils.http import RequestUtils
from app.utils.singleton import Singleton from app.utils.singleton import Singleton
@@ -196,6 +197,12 @@ class U115Pan(StorageBase, metaclass=Singleton):
logger.error(f"创建115目录失败:{str(e)}") logger.error(f"创建115目录失败:{str(e)}")
return None return None
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
TODO 获取目录,不存在则创建
"""
pass
def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]: def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]:
""" """
获取文件详情 获取文件详情
@@ -228,17 +235,23 @@ class U115Pan(StorageBase, metaclass=Singleton):
logger.error(f"重命名115文件失败:{str(e)}") logger.error(f"重命名115文件失败:{str(e)}")
return False return False
def download(self, fileitem: schemas.FileItem) -> Optional[DownloadTicket]: def download(self, fileitem: schemas.FileItem, path: Path) -> bool:
""" """
获取下载链接 获取下载链接
""" """
if not self.__init_cloud(): if not self.__init_cloud():
return None return False
try: try:
return self.cloud.storage().request_download(fileitem.pickcode) ticket = self.cloud.storage().request_download(fileitem.pickcode)
if ticket:
res = RequestUtils(headers=ticket.headers).get_res(ticket.url)
if res:
with open(path, "wb") as f:
f.write(res.content)
return True
except Exception as e: except Exception as e:
logger.error(f"115下载失败:{str(e)}") logger.error(f"115下载失败:{str(e)}")
return None return False
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]: def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
""" """
-49
View File
@@ -3,7 +3,6 @@ import os
import platform import platform
import re import re
import shutil import shutil
import subprocess
import sys import sys
from pathlib import Path from pathlib import Path
from typing import List, Union, Tuple from typing import List, Union, Tuple
@@ -144,54 +143,6 @@ class SystemUtils:
print(str(err)) print(str(err))
return -1, str(err) return -1, str(err)
@staticmethod
def rclone_move(src: Path, dest: Path):
"""
Rclone移动
"""
try:
retcode = subprocess.run(
[
'rclone', 'moveto',
str(src),
f'MP:{dest}'
],
startupinfo=SystemUtils.__get_hidden_shell()
).returncode
return retcode, ""
except Exception as err:
print(str(err))
return -1, str(err)
@staticmethod
def rclone_copy(src: Path, dest: Path):
"""
Rclone复制
"""
try:
retcode = subprocess.run(
[
'rclone', 'copyto',
str(src),
f'MP:{dest}'
],
startupinfo=SystemUtils.__get_hidden_shell()
).returncode
return retcode, ""
except Exception as err:
print(str(err))
return -1, str(err)
@staticmethod
def __get_hidden_shell():
if SystemUtils.is_windows():
st = subprocess.STARTUPINFO()
st.dwFlags = subprocess.STARTF_USESHOWWINDOW
st.wShowWindow = subprocess.SW_HIDE
return st
else:
return None
@staticmethod @staticmethod
def list_files(directory: Path, extensions: list, min_filesize: int = 0) -> List[Path]: def list_files(directory: Path, extensions: list, min_filesize: int = 0) -> List[Path]:
""" """