fix lot of thing
This commit is contained in:
@@ -140,22 +140,22 @@ class DeleteFileThread(QThread):
|
||||
successDelete = pyqtSignal()
|
||||
errorDelete = pyqtSignal(str)
|
||||
|
||||
def __init__(self, fileId: str, fileType: str):
|
||||
def __init__(self, fileUri: str, fileType: str):
|
||||
super().__init__()
|
||||
logger.debug(f"初始化删除文件线程 - ID: {fileId}, 类型: {fileType}")
|
||||
self.fileId = fileId
|
||||
logger.debug(f"初始化删除文件线程 - URI: {fileUri}, 类型: {fileType}")
|
||||
self.fileUri = fileUri
|
||||
self.fileType = fileType
|
||||
|
||||
def run(self):
|
||||
logger.info(f"开始删除文件 - ID: {self.fileId}, 类型: {self.fileType}")
|
||||
logger.info(f"开始删除文件 - URI: {self.fileUri}, 类型: {self.fileType}")
|
||||
try:
|
||||
response = miaoStarsBasicApi.deleteFile(self.fileId, self.fileType)
|
||||
response = miaoStarsBasicApi.deleteFile(self.fileUri, self.fileType)
|
||||
if response["code"] == 0:
|
||||
logger.info(f"文件删除成功 - ID: {self.fileId}")
|
||||
logger.info(f"文件删除成功 - URI: {self.fileUri}")
|
||||
self.successDelete.emit()
|
||||
else:
|
||||
logger.error(
|
||||
f"文件删除失败 - ID: {self.fileId}, 错误: {response.get('msg', '未知错误')}"
|
||||
f"文件删除失败 - URI: {self.fileUri}, 错误: {response.get('msg', '未知错误')}"
|
||||
)
|
||||
self.errorDelete.emit(f"删除失败: {response.get('msg', '未知错误')}")
|
||||
|
||||
@@ -237,7 +237,11 @@ class UploadThread(QThread):
|
||||
self.applicationUrl = "/file/upload"
|
||||
|
||||
self.current_path = policyConfig.returnCurrentPath()
|
||||
self.policy = policyConfig.returnPolicy().get("id")
|
||||
# 获取存储策略,如果为None则使用默认值PqI8
|
||||
self.policy = policyConfig.returnPolicy().get("id", "PqI8")
|
||||
if self.policy is None:
|
||||
self.policy = "PqI8"
|
||||
logger.info("存储策略ID为None,已设置默认值PqI8")
|
||||
|
||||
self.headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36",
|
||||
@@ -262,54 +266,99 @@ class UploadThread(QThread):
|
||||
return mime_type if mime_type else "application/octet-stream"
|
||||
|
||||
def _prepareUploadData(self) -> Dict[str, Any]:
|
||||
"""准备上传数据"""
|
||||
"""准备上传数据,符合Cloudreve V4 API规范"""
|
||||
try:
|
||||
modification_time = os.path.getmtime(self.file_path)
|
||||
size = os.path.getsize(self.file_path)
|
||||
|
||||
# 构建符合API要求的URI格式: cloudreve://my{path}/{name}
|
||||
# 处理路径,确保不会有重复斜杠和前缀
|
||||
# 清理路径,确保不包含cloudreve://my前缀
|
||||
clean_current_path = self.current_path.replace("cloudreve://my", "")
|
||||
# 处理路径,确保不会有重复斜杠
|
||||
path_part = clean_current_path if clean_current_path.startswith('/') else f'/{clean_current_path}'
|
||||
uri = f"cloudreve://my{path_part}/{self.file_name}"
|
||||
|
||||
# 确保路径格式正确,移除重复的前缀
|
||||
uri = uri.replace("cloudreve://my/cloudreve://my", "cloudreve://my")
|
||||
|
||||
# 更健壮地处理重复文件名的情况
|
||||
# 分割路径并去重
|
||||
path_parts = uri.split('/')
|
||||
if len(path_parts) > 1:
|
||||
# 检查最后一个部分是否是文件名
|
||||
if path_parts[-1] == self.file_name:
|
||||
# 检查倒数第二个部分是否也是文件名
|
||||
if len(path_parts) > 2 and path_parts[-2] == self.file_name:
|
||||
# 移除重复的文件名部分
|
||||
path_parts.pop(-2)
|
||||
uri = '/'.join(path_parts)
|
||||
|
||||
logger.info(f"构建上传URI: {uri}")
|
||||
|
||||
return {
|
||||
"path": self.current_path,
|
||||
"uri": uri,
|
||||
"size": size,
|
||||
"name": self.file_name,
|
||||
"policy_id": self.policy,
|
||||
"last_modified": int(modification_time * 1000), # 转换为毫秒
|
||||
"mime_type": self.getMimeType(self.file_path),
|
||||
}
|
||||
except (OSError, ValueError) as e:
|
||||
logger.error(f"准备请求失败: {e}")
|
||||
raise
|
||||
|
||||
def _uploadWithProgress(self, upload_url: str, credential: str, total_size: int):
|
||||
"""带进度显示的上传方法"""
|
||||
try:
|
||||
logger.info("_uploadWithProgress方法开始执行")
|
||||
logger.info(f"参数检查 - URL: {upload_url}, Credential是否存在: {credential is not None}")
|
||||
|
||||
# 处理credential可能为None的情况
|
||||
if credential is None:
|
||||
logger.warning("Credential为None,尝试不使用Authorization头进行上传")
|
||||
# 对于本地存储策略,可能不需要credential
|
||||
auth_header = {}
|
||||
else:
|
||||
auth_header = {"Authorization": credential}
|
||||
|
||||
# 打开文件
|
||||
logger.info(f"开始打开文件: {self.file_path}")
|
||||
self._file_obj = open(self.file_path, "rb")
|
||||
logger.info("文件打开成功")
|
||||
|
||||
# 准备上传头信息
|
||||
upload_headers = {
|
||||
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/140.0.0.0 Safari/537.36",
|
||||
"Accept": "*/*",
|
||||
"Authorization": credential,
|
||||
"Accept-Language": "zh-CN,zh;q=0.9",
|
||||
"Accept-Encoding": "gzip, deflate, br, zstd",
|
||||
"Content-Type": self.getMimeType(self.file_path), # 使用正确的MIME类型
|
||||
"Content-Length": str(total_size), # 添加必需的Content-Length头
|
||||
# 删除硬编码的Origin和Referer,使用miaoStarsBasicApi中已配置的请求头
|
||||
"Sec-Fetch-Dest": "empty",
|
||||
"Sec-Fetch-Mode": "cors",
|
||||
"Sec-Fetch-Site": "cross-site",
|
||||
**auth_header, # 只在credential不为None时添加Authorization头
|
||||
}
|
||||
logger.info(f"上传头信息准备完成: {upload_headers}")
|
||||
|
||||
# 发送预检请求
|
||||
options_result = miaoStarsBasicApi.returnSession().options(
|
||||
upload_url, headers=upload_headers
|
||||
)
|
||||
options_result.raise_for_status()
|
||||
logger.info("OPTIONS预检请求完成")
|
||||
# 尝试发送预检请求,但不阻塞主流程
|
||||
try:
|
||||
logger.info("尝试发送OPTIONS预检请求")
|
||||
options_result = miaoStarsBasicApi.returnSession().options(
|
||||
upload_url, headers=upload_headers, timeout=10
|
||||
)
|
||||
options_result.raise_for_status()
|
||||
logger.info("OPTIONS预检请求完成")
|
||||
except Exception as e:
|
||||
logger.warning(f"OPTIONS预检请求失败,但继续上传: {str(e)}")
|
||||
|
||||
# 创建自定义请求体以支持进度监控和取消
|
||||
class ProgressFileObject:
|
||||
def __init__(
|
||||
self, file_obj, total_size, progress_callback, cancel_check
|
||||
):
|
||||
logger.info("创建ProgressFileObject对象")
|
||||
self.file_obj = file_obj
|
||||
self.total_size = total_size
|
||||
self.progress_callback = progress_callback
|
||||
@@ -319,7 +368,8 @@ class UploadThread(QThread):
|
||||
def read(self, size=-1):
|
||||
# 检查是否取消
|
||||
if self.cancel_check():
|
||||
return
|
||||
logger.info("检测到取消上传请求")
|
||||
raise InterruptedError("Upload cancelled by user")
|
||||
|
||||
data = self.file_obj.read(size)
|
||||
if data:
|
||||
@@ -330,12 +380,14 @@ class UploadThread(QThread):
|
||||
else 0
|
||||
)
|
||||
self.progress_callback(progress, self.uploaded, self.total_size)
|
||||
logger.debug(f"上传进度: {progress:.2f}% ({self.uploaded}/{self.total_size} 字节)")
|
||||
return data
|
||||
|
||||
def __len__(self):
|
||||
return self.total_size
|
||||
|
||||
# 创建带进度监控的文件对象
|
||||
logger.info("创建进度监控文件对象")
|
||||
progress_file = ProgressFileObject(
|
||||
self._file_obj,
|
||||
total_size,
|
||||
@@ -347,17 +399,60 @@ class UploadThread(QThread):
|
||||
|
||||
# 执行实际上传
|
||||
logger.info(f"开始上传文件,总大小: {total_size} 字节")
|
||||
upload_result = miaoStarsBasicApi.returnSession().post(
|
||||
upload_url,
|
||||
data=progress_file,
|
||||
headers=upload_headers,
|
||||
timeout=60,
|
||||
)
|
||||
|
||||
logger.info(f"上传完成,响应状态: {upload_result.status_code}")
|
||||
upload_result.raise_for_status()
|
||||
|
||||
return upload_result
|
||||
logger.info(f"上传URL: {upload_url}")
|
||||
logger.info(f"上传头信息(已脱敏): {{\n 'User-Agent': '...', \n 'Content-Type': '{upload_headers.get("Content-Type")}',\n 'Content-Length': '{upload_headers.get("Content-Length")}',\n 'Authorization': '***' if 'Authorization' in upload_headers else 'None'\n }}")
|
||||
|
||||
# 修复:优化上传逻辑,先尝试PUT方法(Cloudreve V4本地存储通常使用PUT)
|
||||
try:
|
||||
# 首先尝试使用PUT方法
|
||||
logger.info("尝试使用PUT方法上传")
|
||||
upload_result = miaoStarsBasicApi.returnSession().put(
|
||||
upload_url,
|
||||
data=progress_file,
|
||||
headers=upload_headers,
|
||||
timeout=120,
|
||||
stream=True
|
||||
)
|
||||
|
||||
logger.info(f"PUT方法上传完成,响应状态: {upload_result.status_code}")
|
||||
upload_result.raise_for_status()
|
||||
|
||||
return upload_result
|
||||
except InterruptedError:
|
||||
# 用户取消上传
|
||||
raise
|
||||
except Exception as e:
|
||||
logger.error(f"PUT方法上传失败: {str(e)}")
|
||||
# 尝试使用POST方法
|
||||
logger.info("尝试使用POST方法重新上传...")
|
||||
# 重新打开文件
|
||||
if self._file_obj:
|
||||
self._file_obj.close()
|
||||
self._file_obj = open(self.file_path, "rb")
|
||||
|
||||
# 重新创建进度文件对象
|
||||
progress_file = ProgressFileObject(
|
||||
self._file_obj,
|
||||
total_size,
|
||||
lambda progress, uploaded, total: self.uploadProgress.emit(
|
||||
progress, uploaded, total
|
||||
),
|
||||
lambda: self._is_cancelled,
|
||||
)
|
||||
|
||||
# 使用POST方法上传
|
||||
upload_result = miaoStarsBasicApi.returnSession().post(
|
||||
upload_url,
|
||||
data=progress_file,
|
||||
headers=upload_headers,
|
||||
timeout=120,
|
||||
stream=True,
|
||||
)
|
||||
|
||||
logger.info(f"POST方法上传完成,响应状态: {upload_result.status_code}")
|
||||
upload_result.raise_for_status()
|
||||
|
||||
return upload_result
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"上传过程中发生错误: {e}")
|
||||
@@ -379,6 +474,24 @@ class UploadThread(QThread):
|
||||
|
||||
# 准备上传数据
|
||||
upload_data = self._prepareUploadData()
|
||||
|
||||
# 检查policy_id是否为None,如果是则尝试获取默认策略
|
||||
if upload_data.get('policy_id') is None:
|
||||
logger.warning("存储策略ID为None,尝试获取默认存储策略")
|
||||
try:
|
||||
# 获取用户的存储策略列表
|
||||
policies_response = miaoStarsBasicApi.getPolicy()
|
||||
if policies_response.get('code') == 0 and policies_response.get('data'):
|
||||
# 使用第一个可用的策略
|
||||
default_policy = policies_response['data'][0]
|
||||
upload_data['policy_id'] = default_policy.get('id')
|
||||
logger.info(f"已设置默认存储策略: {default_policy.get('name')} (ID: {upload_data['policy_id']})")
|
||||
else:
|
||||
logger.warning("无法获取存储策略列表,使用硬编码默认策略PqI8")
|
||||
upload_data['policy_id'] = 'PqI8'
|
||||
except Exception as e:
|
||||
logger.error(f"获取存储策略失败: {str(e)},使用硬编码默认策略PqI8")
|
||||
upload_data['policy_id'] = 'PqI8'
|
||||
|
||||
# 检查是否取消
|
||||
if self._is_cancelled:
|
||||
@@ -387,41 +500,126 @@ class UploadThread(QThread):
|
||||
|
||||
# 执行上传请求获取上传URL
|
||||
logger.info("请求上传URL")
|
||||
# 从miaoStarsBasicApi获取完整URL(去掉/api/v4后缀)
|
||||
base_url = miaoStarsBasicApi.basicApi.rstrip('/api/v4') # 使用API类中定义的基础URL
|
||||
full_url = f"{base_url}{self.applicationUrl}"
|
||||
# 直接使用basicApi构建完整URL,保留/api/v4后缀
|
||||
# Cloudreve V4 API要求上传接口路径为/api/v4/file/upload
|
||||
full_url = f"{miaoStarsBasicApi.basicApi}{self.applicationUrl}"
|
||||
|
||||
logger.info(f"构建的上传请求URL: {full_url}")
|
||||
logger.info(f"上传请求数据: {upload_data}")
|
||||
|
||||
response = miaoStarsBasicApi.returnSession().put(
|
||||
full_url, json=upload_data, headers=self.headers, timeout=30
|
||||
)
|
||||
|
||||
# 记录响应状态码和内容
|
||||
logger.info(f"上传URL请求响应状态码: {response.status_code}")
|
||||
response_text = response.text
|
||||
logger.info(f"上传URL请求响应内容长度: {len(response_text)} 字符")
|
||||
|
||||
# 如果响应内容较长,只记录前100个字符
|
||||
if len(response_text) > 100:
|
||||
logger.info(f"上传URL请求响应内容预览: {response_text[:100]}...")
|
||||
else:
|
||||
logger.info(f"上传URL请求响应内容: {response_text}")
|
||||
|
||||
response.raise_for_status()
|
||||
|
||||
result = response.json()
|
||||
# 添加错误处理,检查响应是否为有效的JSON
|
||||
try:
|
||||
result = response.json()
|
||||
logger.info(f"JSON解析成功,响应code: {result.get('code', '未找到code字段')}")
|
||||
except ValueError as e:
|
||||
logger.error(f"JSON解析失败: {e}")
|
||||
logger.error(f"无法解析的响应内容: {response_text}")
|
||||
# 抛出异常让上层处理
|
||||
raise ValueError(f"无效的响应格式: {response_text}")
|
||||
|
||||
if result.get("code") == 0:
|
||||
self.uploadApplicationApprovedSignal.emit()
|
||||
upload_urls = result.get("data").get("uploadURLs")
|
||||
credential = result.get("data").get("credential")
|
||||
data = result.get("data", {})
|
||||
upload_urls = data.get("upload_urls") # 使用正确的字段名,符合API规范
|
||||
credential = data.get("credential")
|
||||
session_id = data.get("session_id")
|
||||
storage_policy = data.get("storage_policy", {})
|
||||
policy_type = storage_policy.get("type", "")
|
||||
|
||||
logger.info(f"获取到上传URL数量: {len(upload_urls) if upload_urls else 0}")
|
||||
logger.debug(f"存储策略类型: {policy_type}, Session ID: {session_id}")
|
||||
|
||||
self.uploadUrl = upload_urls[0] + "?chunk=0"
|
||||
logger.info("获取到上传URL")
|
||||
# 优先检查是否有session_id,因为这是必须的
|
||||
if not session_id:
|
||||
logger.error("未获取到session_id,无法构建上传URL")
|
||||
self.uploadFailed.emit("未获取到session_id,无法构建上传URL")
|
||||
return
|
||||
|
||||
# 根据存储策略类型和API响应构建正确的上传URL
|
||||
if upload_urls and len(upload_urls) > 0:
|
||||
# 对于远程存储策略,使用返回的upload_urls,并正确设置chunk参数
|
||||
# 注意:根据URL情况决定是否添加?或&
|
||||
if "?" in upload_urls[0]:
|
||||
self.uploadUrl = upload_urls[0] + "&chunk=0"
|
||||
else:
|
||||
self.uploadUrl = upload_urls[0] + "?chunk=0"
|
||||
logger.info(f"使用远程存储上传URL: {self.uploadUrl}")
|
||||
else:
|
||||
# 对于本地存储策略或没有提供upload_urls的情况,使用session_id构建上传URL
|
||||
# 正确的API路径格式: /file/upload/{sessionId}/{index}
|
||||
# 修复:分块索引应该从0开始,符合Cloudreve V4 API规范
|
||||
self.uploadUrl = f"{miaoStarsBasicApi.basicApi}/file/upload/{session_id}/0"
|
||||
logger.info(f"使用本地存储分块上传路径: {self.uploadUrl}")
|
||||
logger.info(f"获取到上传URL: {self.uploadUrl}")
|
||||
|
||||
# 获取文件总大小
|
||||
total_size = os.path.getsize(self.file_path)
|
||||
|
||||
# 执行带进度显示的上传
|
||||
upload_result = self._uploadWithProgress(
|
||||
self.uploadUrl, credential, total_size
|
||||
)
|
||||
logger.info("开始调用_uploadWithProgress方法执行上传")
|
||||
try:
|
||||
upload_result = self._uploadWithProgress(
|
||||
self.uploadUrl, credential, total_size
|
||||
)
|
||||
logger.info("_uploadWithProgress方法调用完成")
|
||||
|
||||
# 检查是否取消
|
||||
if self._is_cancelled:
|
||||
self.uploadCancelled.emit()
|
||||
# 检查是否取消
|
||||
if self._is_cancelled:
|
||||
self.uploadCancelled.emit()
|
||||
return
|
||||
|
||||
# 处理上传结果
|
||||
logger.info(f"上传响应状态码: {upload_result.status_code}")
|
||||
# 限制日志长度,避免日志过大
|
||||
response_text = upload_result.text
|
||||
if len(response_text) > 100:
|
||||
logger.info(f"上传响应内容预览: {response_text[:100]}...")
|
||||
else:
|
||||
logger.info(f"上传响应内容: {response_text}")
|
||||
|
||||
# 确保上传成功
|
||||
upload_result.raise_for_status()
|
||||
except Exception as e:
|
||||
logger.error(f"上传过程中发生异常: {str(e)}")
|
||||
logger.error(f"异常类型: {type(e).__name__}")
|
||||
self.uploadFailed.emit(f"上传过程中发生错误: {str(e)}")
|
||||
return
|
||||
|
||||
# 处理上传结果
|
||||
logger.info(f"上传响应: {upload_result}")
|
||||
self.uploadFinished.emit()
|
||||
|
||||
try:
|
||||
# 解析上传响应
|
||||
upload_response = upload_result.json()
|
||||
logger.info(f"上传响应JSON解析成功: {upload_response}")
|
||||
|
||||
# 检查上传是否成功完成
|
||||
if upload_response.get('code') == 0:
|
||||
logger.info("文件上传成功完成")
|
||||
self.uploadFinished.emit()
|
||||
else:
|
||||
error_msg = upload_response.get('msg', '上传失败')
|
||||
logger.error(f"上传失败: {error_msg}")
|
||||
self.uploadFailed.emit(error_msg)
|
||||
except ValueError as e:
|
||||
# 如果响应不是有效的JSON,只要状态码成功,也视为上传成功
|
||||
logger.warning(f"上传响应不是有效的JSON: {e}")
|
||||
logger.info("基于成功状态码,假设上传成功")
|
||||
self.uploadFinished.emit()
|
||||
|
||||
else:
|
||||
error_msg = result.get("msg", "上传失败")
|
||||
|
||||
Reference in New Issue
Block a user