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()
# 浏览一次文件确定token正确性
aliyunhelper.list_files()
# 查询用户信息返回
info = aliyunhelper.get_user_info()
info = aliyunhelper.user_info()
if info:
return schemas.Response(success=True, data=info)
return schemas.Response(success=False)
@ -76,7 +74,7 @@ def list_aliyun(fileitem: schemas.FileItem,
if sort == "time":
sort = "updated_at"
if fileitem.type == "file":
fileinfo = AliyunHelper().get_file_detail(fileitem.fileid)
fileinfo = AliyunHelper().detail(fileitem.fileid)
if fileinfo:
return [schemas.FileItem(
fileid=fileinfo.get("file_id"),
@ -91,7 +89,7 @@ def list_aliyun(fileitem: schemas.FileItem,
drive_id=fileinfo.get("drive_id"),
)]
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:
return []
return [schemas.FileItem(
@ -131,7 +129,7 @@ def delete_aliyun(fileitem: schemas.FileItem,
"""
if not fileitem.fileid:
return schemas.Response(success=False)
result = AliyunHelper().delete_file(fileitem.fileid)
result = AliyunHelper().delete(fileitem.fileid)
if result:
return schemas.Response(success=True)
return schemas.Response(success=False)
@ -145,7 +143,7 @@ def download_aliyun(fileid: str,
"""
if not fileid:
return schemas.Response(success=False)
url = AliyunHelper().get_download_url(fileid)
url = AliyunHelper().download(fileid)
if 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:
return schemas.Response(success=False)
result = AliyunHelper().rename_file(fileitem.fileid, new_name)
result = AliyunHelper().rename(fileitem.fileid, new_name)
if result:
if recursive:
transferchain = TransferChain()
@ -214,7 +212,7 @@ def image_aliyun(fileid: str, _: schemas.TokenPayload = Depends(verify_uri_token
"""
if not fileid:
return schemas.Response(success=False)
url = AliyunHelper().get_download_url(fileid)
url = AliyunHelper().download_url(fileid)
if 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:
return schemas.Response(success=True, data={
"total": storage_info[0],
@ -87,7 +87,7 @@ def list_115(fileitem: schemas.FileItem,
extension=suffix,
pickcode=fileitem.pickcode
)]
items = U115Helper().list_files(parent_file_id=fileid)
items = U115Helper().list(parent_file_id=fileid)
if not items:
return []
file_list = [schemas.FileItem(
@ -131,7 +131,7 @@ def delete_115(fileitem: schemas.FileItem,
"""
if not fileitem.fileid:
return schemas.Response(success=False)
result = U115Helper().delete_file(fileitem.fileid)
result = U115Helper().delete(fileitem.fileid)
if result:
return schemas.Response(success=True)
return schemas.Response(success=False)
@ -164,7 +164,7 @@ def rename_115(fileitem: schemas.FileItem,
"""
if not fileitem.fileid or not new_name:
return schemas.Response(success=False)
result = U115Helper().rename_file(fileitem.fileid, new_name)
result = U115Helper().rename(fileitem.fileid, new_name)
if result:
if recursive:
transferchain = TransferChain()

View File

@ -2,6 +2,7 @@ import base64
import json
import time
import uuid
from pathlib import Path
from typing import Optional, Tuple, List
from requests import Response
@ -50,7 +51,11 @@ class AliyunHelper:
# 获取下载链接
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):
self.systemconfig = SystemConfigOper()
@ -71,32 +76,32 @@ class AliyunHelper:
if action:
if code == "DeviceSessionSignatureInvalid":
logger.warn("设备已失效,正在重新建立会话...")
self.create_session(self.get_headers(self.auth_params))
self.__create_session(self.get_headers(self.__auth_params))
if code == "UserDeviceOffline":
logger.warn("设备已离线,尝试重新登录,如仍报错请检查阿里云盘绑定设备数量是否超限!")
self.create_session(self.get_headers(self.auth_params))
self.__create_session(self.get_headers(self.__auth_params))
if code == "AccessTokenInvalid":
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:
logger.info(f"Aliyun {apiname}成功")
@property
def auth_params(self):
def __auth_params(self):
"""
获取阿里云盘认证参数并初始化参数格式
"""
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)
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'),
"updateTime": time.time(),
})
self.update_params(data)
self.get_user_info()
self.__update_params(data)
self.user_info()
except Exception as e:
return {}, f"bizExt 解码失败:{str(e)}"
return data, ""
@ -198,9 +203,9 @@ class AliyunHelper:
code = data.get("code")
if code in ["RefreshTokenExpired", "InvalidParameter.RefreshToken"]:
logger.warn("刷新令牌已过期,请重新登录!")
self.clear_params()
self.__clear_params()
return False
self.update_params({
self.__update_params({
"accessToken": data.get('access_token'),
"expiresIn": data.get('expires_in'),
"updateTime": time.time()
@ -211,10 +216,11 @@ class AliyunHelper:
self.__handle_error(res, "更新令牌", action=False)
return False
def create_session(self, headers: dict):
def __create_session(self, headers: dict):
"""
创建会话
"""
def __os_name():
"""
获取操作系统名称
@ -233,11 +239,12 @@ class AliyunHelper:
})
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:
logger.warn("阿里云盘访问令牌不存在,请先扫码登录!")
return None
@ -246,7 +253,7 @@ class AliyunHelper:
refresh_token = params.get("refreshToken")
if not expires_in or not update_time or not refresh_token:
logger.warn("阿里云盘访问令牌参数错误,请重新扫码登录!")
self.clear_params()
self.__clear_params()
return None
# 是否需要更新设备信息
update_device = False
@ -262,11 +269,11 @@ class AliyunHelper:
if not x_device_id:
x_device_id = uuid.uuid4().hex
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
# 更新设备信息重新创建会话
if update_device:
self.create_session(self.get_headers(params))
self.__create_session(self.get_headers(params))
return params
def get_headers(self, params: dict):
@ -286,18 +293,18 @@ class AliyunHelper:
"x-signature": self._X_SIGNATURE
}
def get_user_info(self) -> dict:
def user_info(self) -> dict:
"""
获取用户信息drive_id等
"""
params = self.get_access_params()
params = self.__access_params
if not params:
return {}
headers = self.get_headers(params)
res = RequestUtils(headers=headers, timeout=10).post_res(self.user_info_url)
if res:
result = res.json()
self.update_params({
self.__update_params({
"resourceDriveId": result.get("resource_drive_id"),
"backDriveId": result.get("backup_drive_id")
})
@ -306,8 +313,8 @@ class AliyunHelper:
self.__handle_error(res, "获取用户信息")
return {}
def list_files(self, drive_id: str = None, parent_file_id: str = 'root', list_type: str = None,
limit: int = 100, order_by: str = 'updated_at') -> List[dict]:
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 返回文件数量默认 50最大 100
@ -315,7 +322,7 @@ class AliyunHelper:
parent_file_id 根目录为root
type all | file | folder
"""
params = self.get_access_params()
params = self.__access_params
if not params:
return []
# 请求头
@ -379,7 +386,7 @@ class AliyunHelper:
"""
创建目录
"""
params = self.get_access_params()
params = self.__access_params
if not params:
return None
headers = self.get_headers(params)
@ -414,11 +421,11 @@ class AliyunHelper:
self.__handle_error(res, "创建目录")
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:
return False
headers = self.get_headers(params)
@ -432,11 +439,11 @@ class AliyunHelper:
self.__handle_error(res, "删除文件")
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:
return None
headers = self.get_headers(params)
@ -450,11 +457,11 @@ class AliyunHelper:
self.__handle_error(res, "获取文件详情")
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:
return False
headers = self.get_headers(params)
@ -470,11 +477,11 @@ class AliyunHelper:
self.__handle_error(res, "重命名文件")
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:
return None
headers = self.get_headers(params)
@ -492,7 +499,7 @@ class AliyunHelper:
"""
移动文件
"""
params = self.get_access_params()
params = self.__access_params
if not params:
return False
headers = self.get_headers(params)
@ -507,3 +514,52 @@ class AliyunHelper:
else:
self.__handle_error(res, "移动文件")
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
from typing import Optional, Tuple, Generator
import oss2
import py115
from py115 import Cloud
from py115.types import LoginTarget, QrcodeSession, QrcodeStatus, Credential, File, DownloadTicket
@ -26,7 +27,7 @@ class U115Helper(metaclass=Singleton):
"""
初始化Cloud
"""
credential = self.credential
credential = self.__credential
if not credential:
logger.warn("115未登录请先登录")
return False
@ -35,12 +36,12 @@ class U115Helper(metaclass=Singleton):
self.cloud = py115.connect(credential)
except Exception as err:
logger.error(f"115连接失败请重新扫码登录{str(err)}")
self.clear_credential()
self.__clear_credential()
return False
return True
@property
def credential(self) -> Optional[Credential]:
def __credential(self) -> Optional[Credential]:
"""
获取已保存的115认证参数
"""
@ -49,13 +50,13 @@ class U115Helper(metaclass=Singleton):
return None
return Credential.from_dict(cookie_dict)
def save_credentail(self, credential: Credential):
def __save_credentail(self, credential: Credential):
"""
设置115认证参数
"""
self.systemconfig.set(SystemConfigKey.User115Params, credential.to_dict())
def clear_credential(self):
def __clear_credential(self):
"""
清除115认证参数
"""
@ -91,7 +92,7 @@ class U115Helper(metaclass=Singleton):
status = self.cloud.qrcode_poll(self._session)
if status == QrcodeStatus.Done:
# 确认完成,保存认证信息
self.save_credentail(self.cloud.export_credentail())
self.__save_credentail(self.cloud.export_credentail())
result = {
"status": 1,
"tip": "登录成功!"
@ -123,7 +124,19 @@ class U115Helper(metaclass=Singleton):
except Exception as 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)}")
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)}")
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)}")
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:
"""
移动文件
@ -209,3 +210,40 @@ class U115Helper(metaclass=Singleton):
except Exception as e:
logger.error(f"移动115文件失败{str(e)}")
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
Pinyin2Hanzi~=0.1.1
pywebpush~=2.0.0
py115~=0.0.4
py115~=0.0.4
oss2~=2.18.6