feat:整理手动中止功能

This commit is contained in:
jxxghp
2025-08-24 19:17:41 +08:00
parent 88f4428ff0
commit f7cd6eac50
7 changed files with 68 additions and 24 deletions
+3 -1
View File
@@ -8,7 +8,7 @@ from app import schemas
from app.chain.media import MediaChain from app.chain.media import MediaChain
from app.chain.storage import StorageChain from app.chain.storage import StorageChain
from app.chain.transfer import TransferChain from app.chain.transfer import TransferChain
from app.core.config import settings from app.core.config import settings, global_vars
from app.core.metainfo import MetaInfoPath from app.core.metainfo import MetaInfoPath
from app.core.security import verify_token, verify_apitoken from app.core.security import verify_token, verify_apitoken
from app.db import get_db from app.db import get_db
@@ -75,6 +75,8 @@ async def remove_queue(fileitem: schemas.FileItem, _: schemas.TokenPayload = Dep
:param _: Token校验 :param _: Token校验
""" """
TransferChain().remove_from_queue(fileitem) TransferChain().remove_from_queue(fileitem)
# 取消整理
global_vars.stop_transfer(fileitem.path)
return schemas.Response(success=True) return schemas.Response(success=True)
+21 -1
View File
@@ -798,6 +798,8 @@ class GlobalVar(object):
SUBSCRIPTIONS: List[dict] = [] SUBSCRIPTIONS: List[dict] = []
# 需应急停止的工作流 # 需应急停止的工作流
EMERGENCY_STOP_WORKFLOWS: List[int] = [] EMERGENCY_STOP_WORKFLOWS: List[int] = []
# 需应急停止文件整理
EMERGENCY_STOP_TRANSFER: List[str] = []
def stop_system(self): def stop_system(self):
""" """
@@ -838,12 +840,30 @@ class GlobalVar(object):
if workflow_id in self.EMERGENCY_STOP_WORKFLOWS: if workflow_id in self.EMERGENCY_STOP_WORKFLOWS:
self.EMERGENCY_STOP_WORKFLOWS.remove(workflow_id) self.EMERGENCY_STOP_WORKFLOWS.remove(workflow_id)
def is_workflow_stopped(self, workflow_id: int): def is_workflow_stopped(self, workflow_id: int) -> bool:
""" """
是否停止工作流 是否停止工作流
""" """
return self.is_system_stopped or workflow_id in self.EMERGENCY_STOP_WORKFLOWS return self.is_system_stopped or workflow_id in self.EMERGENCY_STOP_WORKFLOWS
def stop_transfer(self, path: str):
"""
停止文件整理
"""
if path not in self.EMERGENCY_STOP_TRANSFER:
self.EMERGENCY_STOP_TRANSFER.append(path)
def is_transfer_stopped(self, path: str) -> bool:
"""
是否停止文件整理
"""
if self.is_system_stopped:
return True
if path in self.EMERGENCY_STOP_TRANSFER:
self.EMERGENCY_STOP_TRANSFER.remove(path)
return True
return False
# 全局标识 # 全局标识
global_vars = GlobalVar() global_vars = GlobalVar()
+9 -6
View File
@@ -9,7 +9,7 @@ from typing import List, Optional, Tuple, Union
import requests import requests
from app import schemas from app import schemas
from app.core.config import settings from app.core.config import settings, global_vars
from app.log import logger from app.log import logger
from app.modules.filemanager import StorageBase from app.modules.filemanager import StorageBase
from app.modules.filemanager.storages import transfer_process from app.modules.filemanager.storages import transfer_process
@@ -628,9 +628,12 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
uploaded_size = 0 uploaded_size = 0
with open(local_path, 'rb') as f: with open(local_path, 'rb') as f:
for part_info in part_info_list: for part_info in part_info_list:
part_num = part_info['part_number'] if global_vars.is_transfer_stopped(local_path.as_posix()):
logger.info(f"【阿里云盘】{target_name} 上传已取消!")
return None
# 计算分片参数 # 计算分片参数
part_num = part_info['part_number']
start = (part_num - 1) * chunk_size start = (part_num - 1) * chunk_size
end = min(start + chunk_size, file_size) end = min(start + chunk_size, file_size)
current_chunk_size = end - start current_chunk_size = end - start
@@ -656,7 +659,6 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
upload_url = new_urls[0]['upload_url'] upload_url = new_urls[0]['upload_url']
else: else:
upload_url = part_info['upload_url'] upload_url = part_info['upload_url']
# 执行上传 # 执行上传
logger.info( logger.info(
f"【阿里云盘】开始 第{attempt + 1}次 上传 {target_name} 分片 {part_num} ...") f"【阿里云盘】开始 第{attempt + 1}次 上传 {target_name} 分片 {part_num} ...")
@@ -725,14 +727,15 @@ class AliPan(StorageBase, metaclass=WeakSingleton):
with requests.get(download_url, stream=True) as r: with requests.get(download_url, stream=True) as r:
r.raise_for_status() r.raise_for_status()
downloaded_size = 0 downloaded_size = 0
with open(local_path, "wb") as f: with open(local_path, "wb") as f:
for chunk in r.iter_content(chunk_size=self.chunk_size): for chunk in r.iter_content(chunk_size=self.chunk_size):
if global_vars.is_transfer_stopped(fileitem.path):
logger.info(f"【阿里云盘】{fileitem.path} 下载已取消!")
return None
if chunk: if chunk:
f.write(chunk) f.write(chunk)
downloaded_size += len(chunk)
# 更新进度 # 更新进度
downloaded_size += len(chunk)
if file_size: if file_size:
progress = (downloaded_size * 100) / file_size progress = (downloaded_size * 100) / file_size
progress_callback(progress) progress_callback(progress)
+7 -1
View File
@@ -7,7 +7,7 @@ import requests
from app import schemas from app import schemas
from app.core.cache import cached from app.core.cache import cached
from app.core.config import settings from app.core.config import settings, global_vars
from app.log import logger from app.log import logger
from app.modules.filemanager.storages import StorageBase, transfer_process from app.modules.filemanager.storages import StorageBase, transfer_process
from app.schemas.types import StorageSchema from app.schemas.types import StorageSchema
@@ -561,6 +561,9 @@ class Alist(StorageBase, metaclass=WeakSingleton):
r.raise_for_status() r.raise_for_status()
with open(local_path, "wb") as f: with open(local_path, "wb") as f:
for chunk in r.iter_content(chunk_size=8192): for chunk in r.iter_content(chunk_size=8192):
if global_vars.is_transfer_stopped(fileitem.path):
logger.info(f"【OpenList】{fileitem.path} 下载已取消!")
return None
f.write(chunk) f.write(chunk)
if local_path.exists(): if local_path.exists():
@@ -601,6 +604,9 @@ class Alist(StorageBase, metaclass=WeakSingleton):
self.file_size = file_path.stat().st_size self.file_size = file_path.stat().st_size
def read(self, size=-1): def read(self, size=-1):
if global_vars.is_transfer_stopped(path.as_posix()):
logger.info(f"【OpenList】{path} 上传已取消!")
return None
chunk = self.file.read(size) chunk = self.file.read(size)
if chunk: if chunk:
self.uploaded_size += len(chunk) self.uploaded_size += len(chunk)
+13 -9
View File
@@ -3,6 +3,7 @@ from pathlib import Path
from typing import Optional, List from typing import Optional, List
from app import schemas from app import schemas
from app.core.config import global_vars
from app.helper.directory import DirectoryHelper from app.helper.directory import DirectoryHelper
from app.log import logger from app.log import logger
from app.modules.filemanager.storages import StorageBase, transfer_process from app.modules.filemanager.storages import StorageBase, transfer_process
@@ -98,7 +99,7 @@ class LocalStorage(StorageBase):
# 遍历目录 # 遍历目录
path_obj = Path(path) path_obj = Path(path)
if not path_obj.exists(): if not path_obj.exists():
logger.warn(f"local】目录不存在:{path}") logger.warn(f"本地】目录不存在:{path}")
return [] return []
# 如果是文件 # 如果是文件
@@ -170,7 +171,7 @@ class LocalStorage(StorageBase):
else: else:
shutil.rmtree(path_obj, ignore_errors=True) shutil.rmtree(path_obj, ignore_errors=True)
except Exception as e: except Exception as e:
logger.error(f"local】删除文件失败:{e}") logger.error(f"本地】删除文件失败:{e}")
return False return False
return True return True
@@ -184,7 +185,7 @@ class LocalStorage(StorageBase):
try: try:
path_obj.rename(path_obj.parent / name) path_obj.rename(path_obj.parent / name)
except Exception as e: except Exception as e:
logger.error(f"local】重命名文件失败:{e}") logger.error(f"本地】重命名文件失败:{e}")
return False return False
return True return True
@@ -204,6 +205,9 @@ class LocalStorage(StorageBase):
try: try:
with open(src, "rb") as fsrc, open(dest, "wb") as fdst: with open(src, "rb") as fsrc, open(dest, "wb") as fdst:
while True: while True:
if global_vars.is_transfer_stopped(src.as_posix()):
logger.info(f"【本地】{src} 复制已取消!")
return False
buf = fsrc.read(self.chunk_size) buf = fsrc.read(self.chunk_size)
if not buf: if not buf:
break break
@@ -217,7 +221,7 @@ class LocalStorage(StorageBase):
shutil.copystat(src, dest) shutil.copystat(src, dest)
return True return True
except Exception as e: except Exception as e:
logger.error(f"local】复制文件 {src} 失败:{e}") logger.error(f"本地】复制文件 {src} 失败:{e}")
return False return False
finally: finally:
progress_callback(100) progress_callback(100)
@@ -239,7 +243,7 @@ class LocalStorage(StorageBase):
path.unlink() path.unlink()
return self.get_item(target_path) return self.get_item(target_path)
except Exception as err: except Exception as err:
logger.error(f"local】移动文件失败:{err}") logger.error(f"本地】移动文件失败:{err}")
return None return None
def copy( def copy(
@@ -257,7 +261,7 @@ class LocalStorage(StorageBase):
if self._copy_with_progress(src, dest): if self._copy_with_progress(src, dest):
return True return True
except Exception as err: except Exception as err:
logger.error(f"local】复制文件失败:{err}") logger.error(f"本地】复制文件失败:{err}")
return False return False
def move( def move(
@@ -277,7 +281,7 @@ class LocalStorage(StorageBase):
src.unlink() src.unlink()
return True return True
except Exception as err: except Exception as err:
logger.error(f"local】移动文件失败:{err}") logger.error(f"本地】移动文件失败:{err}")
return False return False
def link(self, fileitem: schemas.FileItem, target_file: Path) -> bool: def link(self, fileitem: schemas.FileItem, target_file: Path) -> bool:
@@ -287,7 +291,7 @@ class LocalStorage(StorageBase):
file_path = Path(fileitem.path) file_path = Path(fileitem.path)
code, message = SystemUtils.link(file_path, target_file) code, message = SystemUtils.link(file_path, target_file)
if code != 0: if code != 0:
logger.error(f"local】硬链接文件失败:{message}") logger.error(f"本地】硬链接文件失败:{message}")
return False return False
return True return True
@@ -298,7 +302,7 @@ class LocalStorage(StorageBase):
file_path = Path(fileitem.path) file_path = Path(fileitem.path)
code, message = SystemUtils.softlink(file_path, target_file) code, message = SystemUtils.softlink(file_path, target_file)
if code != 0: if code != 0:
logger.error(f"local】软链接文件失败:{message}") logger.error(f"本地】软链接文件失败:{message}")
return False return False
return True return True
+7 -3
View File
@@ -8,7 +8,7 @@ from smbclient import ClientConfig, register_session, reset_connection_cache
from smbprotocol.exceptions import SMBException, SMBResponseException, SMBAuthenticationError from smbprotocol.exceptions import SMBException, SMBResponseException, SMBAuthenticationError
from app import schemas from app import schemas
from app.core.config import settings from app.core.config import settings, global_vars
from app.log import logger from app.log import logger
from app.modules.filemanager import StorageBase from app.modules.filemanager import StorageBase
from app.modules.filemanager.storages import transfer_process from app.modules.filemanager.storages import transfer_process
@@ -438,12 +438,14 @@ class SMB(StorageBase, metaclass=WeakSingleton):
with open(local_path, "wb") as dst_file: with open(local_path, "wb") as dst_file:
downloaded_size = 0 downloaded_size = 0
while True: while True:
if global_vars.is_transfer_stopped(fileitem.path):
logger.info(f"【SMB】{fileitem.path} 下载已取消!")
return None
chunk = src_file.read(self.chunk_size) chunk = src_file.read(self.chunk_size)
if not chunk: if not chunk:
break break
dst_file.write(chunk) dst_file.write(chunk)
downloaded_size += len(chunk) downloaded_size += len(chunk)
# 更新进度 # 更新进度
if file_size: if file_size:
progress = (downloaded_size * 100) / file_size progress = (downloaded_size * 100) / file_size
@@ -485,12 +487,14 @@ class SMB(StorageBase, metaclass=WeakSingleton):
with smbclient.open_file(smb_path, mode="wb") as dst_file: with smbclient.open_file(smb_path, mode="wb") as dst_file:
uploaded_size = 0 uploaded_size = 0
while True: while True:
if global_vars.is_transfer_stopped(path.as_posix()):
logger.info(f"【SMB】{path} 上传已取消!")
return None
chunk = src_file.read(self.chunk_size) chunk = src_file.read(self.chunk_size)
if not chunk: if not chunk:
break break
dst_file.write(chunk) dst_file.write(chunk)
uploaded_size += len(chunk) uploaded_size += len(chunk)
# 更新进度 # 更新进度
if file_size: if file_size:
progress = (uploaded_size * 100) / file_size progress = (uploaded_size * 100) / file_size
+7 -2
View File
@@ -12,7 +12,7 @@ from oss2 import SizedFileAdapter, determine_part_size
from oss2.models import PartInfo from oss2.models import PartInfo
from app import schemas from app import schemas
from app.core.config import settings from app.core.config import settings, global_vars
from app.log import logger from app.log import logger
from app.modules.filemanager import StorageBase from app.modules.filemanager import StorageBase
from app.modules.filemanager.storages import transfer_process from app.modules.filemanager.storages import transfer_process
@@ -532,6 +532,9 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
part_number = 1 part_number = 1
offset = 0 offset = 0
while offset < file_size: while offset < file_size:
if global_vars.is_transfer_stopped(local_path.as_posix()):
logger.info(f"【115】{local_path} 上传已取消!")
return None
num_to_upload = min(part_size, file_size - offset) num_to_upload = min(part_size, file_size - offset)
# 调用SizedFileAdapter(fileobj, size)方法会生成一个新的文件对象,重新计算起始追加位置。 # 调用SizedFileAdapter(fileobj, size)方法会生成一个新的文件对象,重新计算起始追加位置。
logger.info(f"【115】开始上传 {target_name} 分片 {part_number}: {offset} -> {offset + num_to_upload}") logger.info(f"【115】开始上传 {target_name} 分片 {part_number}: {offset} -> {offset + num_to_upload}")
@@ -614,10 +617,12 @@ class U115Pan(StorageBase, metaclass=WeakSingleton):
with open(local_path, "wb") as f: with open(local_path, "wb") as f:
for chunk in r.iter_content(chunk_size=self.chunk_size): for chunk in r.iter_content(chunk_size=self.chunk_size):
if global_vars.is_transfer_stopped(fileitem.path):
logger.info(f"【115】{fileitem.path} 下载已取消!")
return None
if chunk: if chunk:
f.write(chunk) f.write(chunk)
downloaded_size += len(chunk) downloaded_size += len(chunk)
# 更新进度 # 更新进度
if file_size: if file_size:
progress = (downloaded_size * 100) / file_size progress = (downloaded_size * 100) / file_size