add upload api

This commit is contained in:
jxxghp 2024-06-21 08:08:23 +08:00
parent ae0e171dd2
commit 0133c6e60c
5 changed files with 162 additions and 69 deletions

View File

@ -47,10 +47,8 @@ def userinfo(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
查询用户信息 查询用户信息
""" """
aliyunhelper = AliyunHelper() aliyunhelper = AliyunHelper()
# 浏览一次文件确定token正确性
aliyunhelper.list_files()
# 查询用户信息返回 # 查询用户信息返回
info = aliyunhelper.get_user_info() info = aliyunhelper.user_info()
if info: if info:
return schemas.Response(success=True, data=info) return schemas.Response(success=True, data=info)
return schemas.Response(success=False) return schemas.Response(success=False)
@ -76,7 +74,7 @@ def list_aliyun(fileitem: schemas.FileItem,
if sort == "time": if sort == "time":
sort = "updated_at" sort = "updated_at"
if fileitem.type == "file": if fileitem.type == "file":
fileinfo = AliyunHelper().get_file_detail(fileitem.fileid) fileinfo = AliyunHelper().detail(fileitem.fileid)
if fileinfo: if fileinfo:
return [schemas.FileItem( return [schemas.FileItem(
fileid=fileinfo.get("file_id"), fileid=fileinfo.get("file_id"),
@ -91,7 +89,7 @@ def list_aliyun(fileitem: schemas.FileItem,
drive_id=fileinfo.get("drive_id"), drive_id=fileinfo.get("drive_id"),
)] )]
return [] return []
items = AliyunHelper().list_files(drive_id=fileitem.drive_id, parent_file_id=fileitem.fileid, order_by=sort) items = AliyunHelper().list(drive_id=fileitem.drive_id, parent_file_id=fileitem.fileid, order_by=sort)
if not items: if not items:
return [] return []
return [schemas.FileItem( return [schemas.FileItem(
@ -131,7 +129,7 @@ def delete_aliyun(fileitem: schemas.FileItem,
""" """
if not fileitem.fileid: if not fileitem.fileid:
return schemas.Response(success=False) return schemas.Response(success=False)
result = AliyunHelper().delete_file(fileitem.fileid) result = AliyunHelper().delete(fileitem.fileid)
if result: if result:
return schemas.Response(success=True) return schemas.Response(success=True)
return schemas.Response(success=False) return schemas.Response(success=False)
@ -145,7 +143,7 @@ def download_aliyun(fileid: str,
""" """
if not fileid: if not fileid:
return schemas.Response(success=False) return schemas.Response(success=False)
url = AliyunHelper().get_download_url(fileid) url = AliyunHelper().download(fileid)
if url: if url:
# 重定向 # 重定向
return Response(status_code=302, headers={"Location": url}) return Response(status_code=302, headers={"Location": url})
@ -162,7 +160,7 @@ def rename_aliyun(fileitem: schemas.FileItem,
""" """
if not fileitem.fileid or not new_name: if not fileitem.fileid or not new_name:
return schemas.Response(success=False) return schemas.Response(success=False)
result = AliyunHelper().rename_file(fileitem.fileid, new_name) result = AliyunHelper().rename(fileitem.fileid, new_name)
if result: if result:
if recursive: if recursive:
transferchain = TransferChain() transferchain = TransferChain()
@ -214,7 +212,7 @@ def image_aliyun(fileid: str, _: schemas.TokenPayload = Depends(verify_uri_token
""" """
if not fileid: if not fileid:
return schemas.Response(success=False) return schemas.Response(success=False)
url = AliyunHelper().get_download_url(fileid) url = AliyunHelper().download_url(fileid)
if url: if url:
# 重定向 # 重定向
return Response(status_code=302, headers={"Location": url}) return Response(status_code=302, headers={"Location": url})

View File

@ -46,7 +46,7 @@ def storage(_: schemas.TokenPayload = Depends(verify_token)) -> Any:
""" """
查询存储空间信息 查询存储空间信息
""" """
storage_info = U115Helper().get_storage() storage_info = U115Helper().storage()
if storage_info: if storage_info:
return schemas.Response(success=True, data={ return schemas.Response(success=True, data={
"total": storage_info[0], "total": storage_info[0],
@ -87,7 +87,7 @@ def list_115(fileitem: schemas.FileItem,
extension=suffix, extension=suffix,
pickcode=fileitem.pickcode pickcode=fileitem.pickcode
)] )]
items = U115Helper().list_files(parent_file_id=fileid) items = U115Helper().list(parent_file_id=fileid)
if not items: if not items:
return [] return []
file_list = [schemas.FileItem( file_list = [schemas.FileItem(
@ -131,7 +131,7 @@ def delete_115(fileitem: schemas.FileItem,
""" """
if not fileitem.fileid: if not fileitem.fileid:
return schemas.Response(success=False) return schemas.Response(success=False)
result = U115Helper().delete_file(fileitem.fileid) result = U115Helper().delete(fileitem.fileid)
if result: if result:
return schemas.Response(success=True) return schemas.Response(success=True)
return schemas.Response(success=False) return schemas.Response(success=False)
@ -164,7 +164,7 @@ def rename_115(fileitem: schemas.FileItem,
""" """
if not fileitem.fileid or not new_name: if not fileitem.fileid or not new_name:
return schemas.Response(success=False) return schemas.Response(success=False)
result = U115Helper().rename_file(fileitem.fileid, new_name) result = U115Helper().rename(fileitem.fileid, new_name)
if result: if result:
if recursive: if recursive:
transferchain = TransferChain() transferchain = TransferChain()

View File

@ -2,6 +2,7 @@ import base64
import json import json
import time import time
import uuid import uuid
from pathlib import Path
from typing import Optional, Tuple, List from typing import Optional, Tuple, List
from requests import Response from requests import Response
@ -50,7 +51,11 @@ class AliyunHelper:
# 获取下载链接 # 获取下载链接
download_url = "https://api.aliyundrive.com/v2/file/get_download_url" download_url = "https://api.aliyundrive.com/v2/file/get_download_url"
# 移动文件 # 移动文件
move_file_url = "https://api.aliyundrive.com/v3/file/move" move_file_url = "https://api.aliyundrive.com/v2/file/move"
# 创建文件
create_file_url = "https://api.aliyundrive.com/adrive/v2/file/create"
# 上传文件完成
upload_file_complete_url = "https://api.aliyundrive.com/v2/file/complete"
def __init__(self): def __init__(self):
self.systemconfig = SystemConfigOper() self.systemconfig = SystemConfigOper()
@ -71,32 +76,32 @@ class AliyunHelper:
if action: if action:
if code == "DeviceSessionSignatureInvalid": if code == "DeviceSessionSignatureInvalid":
logger.warn("设备已失效,正在重新建立会话...") logger.warn("设备已失效,正在重新建立会话...")
self.create_session(self.get_headers(self.auth_params)) self.__create_session(self.get_headers(self.__auth_params))
if code == "UserDeviceOffline": if code == "UserDeviceOffline":
logger.warn("设备已离线,尝试重新登录,如仍报错请检查阿里云盘绑定设备数量是否超限!") logger.warn("设备已离线,尝试重新登录,如仍报错请检查阿里云盘绑定设备数量是否超限!")
self.create_session(self.get_headers(self.auth_params)) self.__create_session(self.get_headers(self.__auth_params))
if code == "AccessTokenInvalid": if code == "AccessTokenInvalid":
logger.warn("访问令牌已失效,正在刷新令牌...") logger.warn("访问令牌已失效,正在刷新令牌...")
self.__update_accesstoken(self.auth_params, self.auth_params.get("refreshToken")) self.__update_accesstoken(self.__auth_params, self.__auth_params.get("refreshToken"))
else: else:
logger.info(f"Aliyun {apiname}成功") logger.info(f"Aliyun {apiname}成功")
@property @property
def auth_params(self): def __auth_params(self):
""" """
获取阿里云盘认证参数并初始化参数格式 获取阿里云盘认证参数并初始化参数格式
""" """
return self.systemconfig.get(SystemConfigKey.UserAliyunParams) or {} return self.systemconfig.get(SystemConfigKey.UserAliyunParams) or {}
def update_params(self, params: dict): def __update_params(self, params: dict):
""" """
设置阿里云盘认证参数 设置阿里云盘认证参数
""" """
current_params = self.auth_params current_params = self.__auth_params
current_params.update(params) current_params.update(params)
self.systemconfig.set(SystemConfigKey.UserAliyunParams, current_params) self.systemconfig.set(SystemConfigKey.UserAliyunParams, current_params)
def clear_params(self): def __clear_params(self):
""" """
清除阿里云盘认证参数 清除阿里云盘认证参数
""" """
@ -173,8 +178,8 @@ class AliyunHelper:
"defaultDriveId": pds_login_result.get('defaultDriveId'), "defaultDriveId": pds_login_result.get('defaultDriveId'),
"updateTime": time.time(), "updateTime": time.time(),
}) })
self.update_params(data) self.__update_params(data)
self.get_user_info() self.user_info()
except Exception as e: except Exception as e:
return {}, f"bizExt 解码失败:{str(e)}" return {}, f"bizExt 解码失败:{str(e)}"
return data, "" return data, ""
@ -198,9 +203,9 @@ class AliyunHelper:
code = data.get("code") code = data.get("code")
if code in ["RefreshTokenExpired", "InvalidParameter.RefreshToken"]: if code in ["RefreshTokenExpired", "InvalidParameter.RefreshToken"]:
logger.warn("刷新令牌已过期,请重新登录!") logger.warn("刷新令牌已过期,请重新登录!")
self.clear_params() self.__clear_params()
return False return False
self.update_params({ self.__update_params({
"accessToken": data.get('access_token'), "accessToken": data.get('access_token'),
"expiresIn": data.get('expires_in'), "expiresIn": data.get('expires_in'),
"updateTime": time.time() "updateTime": time.time()
@ -211,10 +216,11 @@ class AliyunHelper:
self.__handle_error(res, "更新令牌", action=False) self.__handle_error(res, "更新令牌", action=False)
return False return False
def create_session(self, headers: dict): def __create_session(self, headers: dict):
""" """
创建会话 创建会话
""" """
def __os_name(): def __os_name():
""" """
获取操作系统名称 获取操作系统名称
@ -233,11 +239,12 @@ class AliyunHelper:
}) })
self.__handle_error(res, "创建会话", action=False) self.__handle_error(res, "创建会话", action=False)
def get_access_params(self) -> Optional[dict]: @property
def __access_params(self) -> Optional[dict]:
""" """
获取阿里云盘访问参数如果超时则更新后返回 获取阿里云盘访问参数如果超时则更新后返回
""" """
params = self.auth_params params = self.__auth_params
if not params: if not params:
logger.warn("阿里云盘访问令牌不存在,请先扫码登录!") logger.warn("阿里云盘访问令牌不存在,请先扫码登录!")
return None return None
@ -246,7 +253,7 @@ class AliyunHelper:
refresh_token = params.get("refreshToken") refresh_token = params.get("refreshToken")
if not expires_in or not update_time or not refresh_token: if not expires_in or not update_time or not refresh_token:
logger.warn("阿里云盘访问令牌参数错误,请重新扫码登录!") logger.warn("阿里云盘访问令牌参数错误,请重新扫码登录!")
self.clear_params() self.__clear_params()
return None return None
# 是否需要更新设备信息 # 是否需要更新设备信息
update_device = False update_device = False
@ -262,11 +269,11 @@ class AliyunHelper:
if not x_device_id: if not x_device_id:
x_device_id = uuid.uuid4().hex x_device_id = uuid.uuid4().hex
params['x_device_id'] = x_device_id params['x_device_id'] = x_device_id
self.update_params({"x_device_id": x_device_id}) self.__update_params({"x_device_id": x_device_id})
update_device = True update_device = True
# 更新设备信息重新创建会话 # 更新设备信息重新创建会话
if update_device: if update_device:
self.create_session(self.get_headers(params)) self.__create_session(self.get_headers(params))
return params return params
def get_headers(self, params: dict): def get_headers(self, params: dict):
@ -286,18 +293,18 @@ class AliyunHelper:
"x-signature": self._X_SIGNATURE "x-signature": self._X_SIGNATURE
} }
def get_user_info(self) -> dict: def user_info(self) -> dict:
""" """
获取用户信息drive_id等 获取用户信息drive_id等
""" """
params = self.get_access_params() params = self.__access_params
if not params: if not params:
return {} return {}
headers = self.get_headers(params) headers = self.get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.user_info_url) res = RequestUtils(headers=headers, timeout=10).post_res(self.user_info_url)
if res: if res:
result = res.json() result = res.json()
self.update_params({ self.__update_params({
"resourceDriveId": result.get("resource_drive_id"), "resourceDriveId": result.get("resource_drive_id"),
"backDriveId": result.get("backup_drive_id") "backDriveId": result.get("backup_drive_id")
}) })
@ -306,7 +313,7 @@ class AliyunHelper:
self.__handle_error(res, "获取用户信息") self.__handle_error(res, "获取用户信息")
return {} return {}
def list_files(self, drive_id: str = None, parent_file_id: str = 'root', list_type: str = None, def list(self, drive_id: str = None, parent_file_id: str = 'root', list_type: str = None,
limit: int = 100, order_by: str = 'updated_at') -> List[dict]: limit: int = 100, order_by: str = 'updated_at') -> List[dict]:
""" """
浏览文件 浏览文件
@ -315,7 +322,7 @@ class AliyunHelper:
parent_file_id 根目录为root parent_file_id 根目录为root
type all | file | folder type all | file | folder
""" """
params = self.get_access_params() params = self.__access_params
if not params: if not params:
return [] return []
# 请求头 # 请求头
@ -379,7 +386,7 @@ class AliyunHelper:
""" """
创建目录 创建目录
""" """
params = self.get_access_params() params = self.__access_params
if not params: if not params:
return None return None
headers = self.get_headers(params) headers = self.get_headers(params)
@ -414,11 +421,11 @@ class AliyunHelper:
self.__handle_error(res, "创建目录") self.__handle_error(res, "创建目录")
return None return None
def delete_file(self, file_id: str) -> bool: def delete(self, file_id: str) -> bool:
""" """
删除文件 删除文件
""" """
params = self.get_access_params() params = self.__access_params
if not params: if not params:
return False return False
headers = self.get_headers(params) headers = self.get_headers(params)
@ -432,11 +439,11 @@ class AliyunHelper:
self.__handle_error(res, "删除文件") self.__handle_error(res, "删除文件")
return False return False
def get_file_detail(self, file_id: str) -> Optional[dict]: def detail(self, file_id: str) -> Optional[dict]:
""" """
获取文件详情 获取文件详情
""" """
params = self.get_access_params() params = self.__access_params
if not params: if not params:
return None return None
headers = self.get_headers(params) headers = self.get_headers(params)
@ -450,11 +457,11 @@ class AliyunHelper:
self.__handle_error(res, "获取文件详情") self.__handle_error(res, "获取文件详情")
return None return None
def rename_file(self, file_id: str, name: str) -> bool: def rename(self, file_id: str, name: str) -> bool:
""" """
重命名文件 重命名文件
""" """
params = self.get_access_params() params = self.__access_params
if not params: if not params:
return False return False
headers = self.get_headers(params) headers = self.get_headers(params)
@ -470,11 +477,11 @@ class AliyunHelper:
self.__handle_error(res, "重命名文件") self.__handle_error(res, "重命名文件")
return False return False
def get_download_url(self, file_id: str) -> Optional[str]: def download(self, file_id: str) -> Optional[str]:
""" """
获取下载链接 获取下载链接
""" """
params = self.get_access_params() params = self.__access_params
if not params: if not params:
return None return None
headers = self.get_headers(params) headers = self.get_headers(params)
@ -492,7 +499,7 @@ class AliyunHelper:
""" """
移动文件 移动文件
""" """
params = self.get_access_params() params = self.__access_params
if not params: if not params:
return False return False
headers = self.get_headers(params) headers = self.get_headers(params)
@ -507,3 +514,52 @@ class AliyunHelper:
else: else:
self.__handle_error(res, "移动文件") self.__handle_error(res, "移动文件")
return False return False
def upload(self, parent_file_id: str, name: str, filepath: Path) -> Optional[dict]:
"""
上传文件并标记完成
"""
params = self.__access_params
if not params:
return None
headers = self.get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.create_file_url, json={
"drive_id": params.get("resourceDriveId"),
"parent_file_id": parent_file_id,
"name": name,
"type": "file",
"check_name_mode": "refuse"
})
if not res:
self.__handle_error(res, "创建文件")
return None
# 获取上传参数
result = res.json()
drive_id = result.get("drive_id")
file_id = result.get("file_id")
upload_id = result.get("upload_id")
part_info_list = result.get("part_info_list")
if part_info_list:
# 上传地址
upload_url = part_info_list[0].get("upload_url")
# 上传文件
res = RequestUtils(headers=headers).put_res(upload_url, data=filepath.read_bytes())
if not res:
self.__handle_error(res, "上传文件")
return None
# 标记文件上传完毕
res = RequestUtils(headers=headers, timeout=10).post_res(self.upload_file_complete_url, json={
"drive_id": drive_id,
"file_id": file_id,
"upload_id": upload_id
})
if not res:
self.__handle_error(res, "标记上传状态")
return None
return {
"drive_id": drive_id,
"file_id": file_id
}
else:
logger.warn("上传文件失败:无法获取上传地址!")
return None

View File

@ -1,6 +1,7 @@
import base64 import base64
from typing import Optional, Tuple, Generator from typing import Optional, Tuple, Generator
import oss2
import py115 import py115
from py115 import Cloud from py115 import Cloud
from py115.types import LoginTarget, QrcodeSession, QrcodeStatus, Credential, File, DownloadTicket from py115.types import LoginTarget, QrcodeSession, QrcodeStatus, Credential, File, DownloadTicket
@ -26,7 +27,7 @@ class U115Helper(metaclass=Singleton):
""" """
初始化Cloud 初始化Cloud
""" """
credential = self.credential credential = self.__credential
if not credential: if not credential:
logger.warn("115未登录请先登录") logger.warn("115未登录请先登录")
return False return False
@ -35,12 +36,12 @@ class U115Helper(metaclass=Singleton):
self.cloud = py115.connect(credential) self.cloud = py115.connect(credential)
except Exception as err: except Exception as err:
logger.error(f"115连接失败请重新扫码登录{str(err)}") logger.error(f"115连接失败请重新扫码登录{str(err)}")
self.clear_credential() self.__clear_credential()
return False return False
return True return True
@property @property
def credential(self) -> Optional[Credential]: def __credential(self) -> Optional[Credential]:
""" """
获取已保存的115认证参数 获取已保存的115认证参数
""" """
@ -49,13 +50,13 @@ class U115Helper(metaclass=Singleton):
return None return None
return Credential.from_dict(cookie_dict) return Credential.from_dict(cookie_dict)
def save_credentail(self, credential: Credential): def __save_credentail(self, credential: Credential):
""" """
设置115认证参数 设置115认证参数
""" """
self.systemconfig.set(SystemConfigKey.User115Params, credential.to_dict()) self.systemconfig.set(SystemConfigKey.User115Params, credential.to_dict())
def clear_credential(self): def __clear_credential(self):
""" """
清除115认证参数 清除115认证参数
""" """
@ -91,7 +92,7 @@ class U115Helper(metaclass=Singleton):
status = self.cloud.qrcode_poll(self._session) status = self.cloud.qrcode_poll(self._session)
if status == QrcodeStatus.Done: if status == QrcodeStatus.Done:
# 确认完成,保存认证信息 # 确认完成,保存认证信息
self.save_credentail(self.cloud.export_credentail()) self.__save_credentail(self.cloud.export_credentail())
result = { result = {
"status": 1, "status": 1,
"tip": "登录成功!" "tip": "登录成功!"
@ -123,7 +124,19 @@ class U115Helper(metaclass=Singleton):
except Exception as e: except Exception as e:
return {}, f"115登录确认失败{str(e)}" return {}, f"115登录确认失败{str(e)}"
def list_files(self, parent_file_id: str = '0') -> Optional[Generator[File, None, None]]: def storage(self) -> Optional[Tuple[int, int]]:
"""
获取存储空间
"""
if not self.__init_cloud():
return None
try:
return self.cloud.storage().space()
except Exception as e:
logger.error(f"获取115存储空间失败{str(e)}")
return None
def list(self, parent_file_id: str = '0') -> Optional[Generator[File, None, None]]:
""" """
浏览文件 浏览文件
""" """
@ -147,7 +160,7 @@ class U115Helper(metaclass=Singleton):
logger.error(f"创建115目录失败{str(e)}") logger.error(f"创建115目录失败{str(e)}")
return None return None
def delete_file(self, file_id: str) -> bool: def delete(self, file_id: str) -> bool:
""" """
删除文件 删除文件
""" """
@ -160,7 +173,7 @@ class U115Helper(metaclass=Singleton):
logger.error(f"删除115文件失败{str(e)}") logger.error(f"删除115文件失败{str(e)}")
return False return False
def rename_file(self, file_id: str, name: str) -> bool: def rename(self, file_id: str, name: str) -> bool:
""" """
重命名文件 重命名文件
""" """
@ -185,18 +198,6 @@ class U115Helper(metaclass=Singleton):
logger.error(f"115下载失败{str(e)}") logger.error(f"115下载失败{str(e)}")
return None return None
def get_storage(self) -> Optional[Tuple[int, int]]:
"""
获取存储空间
"""
if not self.__init_cloud():
return None
try:
return self.cloud.storage().space()
except Exception as e:
logger.error(f"获取115存储空间失败{str(e)}")
return None
def move(self, file_id: str, target_id: str) -> bool: def move(self, file_id: str, target_id: str) -> bool:
""" """
移动文件 移动文件
@ -209,3 +210,40 @@ class U115Helper(metaclass=Singleton):
except Exception as e: except Exception as e:
logger.error(f"移动115文件失败{str(e)}") logger.error(f"移动115文件失败{str(e)}")
return False return False
def upload(self, file_path: str, parent_file_id: str) -> Optional[dict]:
"""
上传文件
"""
if not self.__init_cloud():
return None
try:
ticket = self.cloud.storage().request_upload(dir_id=parent_file_id, file_path=file_path)
if ticket is None:
logger.warn(f"115请求上传出错")
return None
elif ticket.is_done:
logger.warn(f"115请求上传失败文件已存在")
return {}
else:
auth = oss2.StsAuth(**ticket.oss_token)
bucket = oss2.Bucket(
auth=auth,
endpoint=ticket.oss_endpoint,
bucket_name=ticket.bucket_name,
)
por = bucket.put_object_from_file(
key=ticket.object_key,
filename=file_path,
headers=ticket.headers,
)
result = por.resp.response.json()
if result:
logger.info(f"115上传文件成功{result}")
return result
else:
logger.warn(f"115上传文件失败{por.resp.response.text}")
return None
except Exception as e:
logger.error(f"上传115文件失败{str(e)}")
return None

View File

@ -58,4 +58,5 @@ pystray~=0.19.5
pyotp~=2.9.0 pyotp~=2.9.0
Pinyin2Hanzi~=0.1.1 Pinyin2Hanzi~=0.1.1
pywebpush~=2.0.0 pywebpush~=2.0.0
py115~=0.0.4 py115~=0.0.4
oss2~=2.18.6