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

View File

@@ -9,7 +9,6 @@ from app.chain.transfer import TransferChain
from app.core.config import settings
from app.core.metainfo import MetaInfoPath
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.schemas.types import ProgressKey

View File

@@ -10,7 +10,6 @@ from app.core.config import settings
from app.core.metainfo import MetaInfoPath
from app.core.security import verify_token, verify_uri_token
from app.helper.progress import ProgressHelper
from app.modules.filetransfer.storage.u115 import U115Helper
from app.schemas.types import ProgressKey
from app.utils.http import RequestUtils

View File

@@ -181,7 +181,7 @@ class FileTransferModule(_ModuleBase):
pass
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: 源文件
@@ -192,29 +192,60 @@ class FileTransferModule(_ModuleBase):
if fileitem.storage != "local" and target_storage != "local":
logger.error(f"不支持 {fileitem.storage}{target_storage} 的文件整理")
return 1
retcode = 0
return False
# 源操作对象
source_oper = self.__get_storage_oper(fileitem.storage)
# 目的操作对象
target_oper = self.__get_storage_oper(target_storage)
with lock:
if fileitem.storage == "local" and target_storage == "local":
# 本地到本地
if transfer_type == "copy":
retcode = source_oper.copy(fileitem, target_file)
return source_oper.copy(fileitem, target_file)
elif transfer_type == "move":
retcode = source_oper.move(fileitem, target_file)
return source_oper.move(fileitem, target_file)
elif transfer_type == "link":
retcode = source_oper.link(fileitem, target_file)
return source_oper.link(fileitem, target_file)
elif transfer_type == "softlink":
retcode = source_oper.softlink(fileitem, target_file)
# TODO 本地到网盘
# TODO 网盘到本地
return retcode
return source_oper.softlink(fileitem, target_file)
elif fileitem.storage == "local" and target_storage != "local":
# 本地到网盘
if transfer_type == "copy":
# 复制
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,
transfer_type: str) -> int:

View File

@@ -45,6 +45,13 @@ class StorageBase(metaclass=ABCMeta):
"""
pass
@abstractmethod
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
获取目录
"""
pass
@abstractmethod
def delete(self, fileitm: schemas.FileItem) -> bool:
"""
@@ -60,9 +67,9 @@ class StorageBase(metaclass=ABCMeta):
pass
@abstractmethod
def download(self, fileitm: schemas.FileItem) -> Any:
def download(self, fileitm: schemas.FileItem, path: Path):
"""
下载链接
下载文件,保存到本地
"""
pass

View File

@@ -452,6 +452,12 @@ class AliPan(StorageBase):
self.__handle_error(res, "创建目录")
return None
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
TODO 获取目录,不存在则创建
"""
pass
def delete(self, fileitem: schemas.FileItem) -> bool:
"""
删除文件
@@ -520,27 +526,35 @@ class AliPan(StorageBase):
self.__handle_error(res, "重命名文件")
return False
def download(self, fileitem: schemas.FileItem) -> Optional[str]:
def download(self, fileitem: schemas.FileItem, path: Path) -> bool:
"""
获取下载链接
下载文件,保存到本地
"""
params = self.__access_params
if not params:
return None
return False
headers = self.__get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.download_url, json={
"drive_id": fileitem.drive_id,
"file_id": fileitem.fileid
})
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:
self.__handle_error(res, "获取下载链接")
return None
return False
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
"""
上传文件,并标记完成
TODO 上传文件分片、秒传
"""
params = self.__access_params
if not params:
@@ -634,10 +648,19 @@ class AliPan(StorageBase):
return False
def copy(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
"""
复制文件
"""
pass
def link(self, fileitm: schemas.FileItem, target_file: Path) -> bool:
"""
硬链接文件
"""
pass
def softlink(self, fileitm: schemas.FileItem, target_file: schemas.FileItem) -> bool:
"""
软链接文件
"""
pass

View File

@@ -2,8 +2,6 @@ import shutil
from pathlib import Path
from typing import Optional, List
from starlette.responses import FileResponse, Response
from app import schemas
from app.log import logger
from app.modules.filetransfer.storage import StorageBase
@@ -117,6 +115,12 @@ class LocalStorage(StorageBase):
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]:
"""
获取文件详情
@@ -156,45 +160,17 @@ class LocalStorage(StorageBase):
return False
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 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
return False
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
"""
上传文件
"""
if not fileitem.path:
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,
)
return None
def copy(self, fileitem: schemas.FileItem, target_file: Path) -> bool:
"""

View File

@@ -1,9 +1,12 @@
import subprocess
from pathlib import Path
from typing import Optional, Any, List
from typing import Optional, List
from app import schemas
from app.log import logger
from app.modules.filetransfer.storage import StorageBase
from app.schemas.types import StorageSchema
from app.utils.system import SystemUtils
class Rclone(StorageBase):
@@ -19,6 +22,16 @@ class Rclone(StorageBase):
"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:
pass
@@ -28,13 +41,19 @@ class Rclone(StorageBase):
def create_folder(self, fileitm: schemas.FileItem, name: str) -> Optional[schemas.FileItem]:
pass
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
获取目录
"""
pass
def delete(self, fileitm: schemas.FileItem) -> bool:
pass
def rename(self, fileitm: schemas.FileItem, name: str) -> bool:
pass
def download(self, fileitm: schemas.FileItem) -> Any:
def download(self, fileitm: schemas.FileItem, path: Path) -> bool:
pass
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]:
pass
def move(self, fileitm: schemas.FileItem, target_dir: schemas.FileItem) -> bool:
pass
def move(self, fileitm: schemas.FileItem, target_file: schemas.FileItem) -> bool:
"""
移动文件
"""
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:
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:
pass

View File

@@ -5,13 +5,14 @@ from typing import Optional, Tuple, List
import oss2
import py115
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.db.systemconfig_oper import SystemConfigOper
from app.log import logger
from app.modules.filetransfer.storage import StorageBase
from app.schemas.types import SystemConfigKey, StorageSchema
from app.utils.http import RequestUtils
from app.utils.singleton import Singleton
@@ -196,6 +197,12 @@ class U115Pan(StorageBase, metaclass=Singleton):
logger.error(f"创建115目录失败{str(e)}")
return None
def get_folder(self, path: Path) -> Optional[schemas.FileItem]:
"""
TODO 获取目录,不存在则创建
"""
pass
def detail(self, fileitm: schemas.FileItem) -> Optional[schemas.FileItem]:
"""
获取文件详情
@@ -228,17 +235,23 @@ class U115Pan(StorageBase, metaclass=Singleton):
logger.error(f"重命名115文件失败{str(e)}")
return False
def download(self, fileitem: schemas.FileItem) -> Optional[DownloadTicket]:
def download(self, fileitem: schemas.FileItem, path: Path) -> bool:
"""
获取下载链接
"""
if not self.__init_cloud():
return None
return False
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:
logger.error(f"115下载失败{str(e)}")
return None
return False
def upload(self, fileitem: schemas.FileItem, path: Path) -> Optional[schemas.FileItem]:
"""

View File

@@ -3,7 +3,6 @@ import os
import platform
import re
import shutil
import subprocess
import sys
from pathlib import Path
from typing import List, Union, Tuple
@@ -144,54 +143,6 @@ class SystemUtils:
print(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
def list_files(directory: Path, extensions: list, min_filesize: int = 0) -> List[Path]:
"""