feat: support upload image api
- support upload and delete image of people - support uploads any image and get link after login
This commit is contained in:
@@ -7,6 +7,9 @@ class ErrorCode(Enum):
|
|||||||
SUCCESS = 0
|
SUCCESS = 0
|
||||||
MODEL_ERROR = 1000
|
MODEL_ERROR = 1000
|
||||||
RLDB_ERROR = 2100
|
RLDB_ERROR = 2100
|
||||||
|
OBS_ERROR = 3100
|
||||||
|
OBS_INPUT_ERROR = 3102
|
||||||
|
OBS_SERVICE_ERROR = 3103
|
||||||
|
|
||||||
class error(Protocol):
|
class error(Protocol):
|
||||||
_error_code: int = 0
|
_error_code: int = 0
|
||||||
|
|||||||
@@ -4,11 +4,13 @@ import logging
|
|||||||
from typing import Protocol
|
from typing import Protocol
|
||||||
import qiniu
|
import qiniu
|
||||||
import requests
|
import requests
|
||||||
|
|
||||||
|
from .error import ErrorCode, error
|
||||||
from .config import get_instance as get_config
|
from .config import get_instance as get_config
|
||||||
|
|
||||||
|
|
||||||
class OBS(Protocol):
|
class OBS(Protocol):
|
||||||
def Put(self, obs_path: str, content: bytes) -> str:
|
def put(self, obs_path: str, content: bytes) -> str:
|
||||||
"""
|
"""
|
||||||
上传文件到OBS
|
上传文件到OBS
|
||||||
|
|
||||||
@@ -21,7 +23,7 @@ class OBS(Protocol):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
def Get(self, obs_path: str) -> bytes:
|
def get(self, obs_path: str) -> bytes:
|
||||||
"""
|
"""
|
||||||
从OBS下载文件
|
从OBS下载文件
|
||||||
|
|
||||||
@@ -33,7 +35,7 @@ class OBS(Protocol):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
def List(self, obs_path: str) -> list:
|
def list(self, obs_path: str) -> list:
|
||||||
"""
|
"""
|
||||||
列出OBS目录下的所有文件
|
列出OBS目录下的所有文件
|
||||||
|
|
||||||
@@ -45,7 +47,7 @@ class OBS(Protocol):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
def Del(self, obs_path: str) -> bool:
|
def delete(self, obs_path: str) -> error:
|
||||||
"""
|
"""
|
||||||
删除OBS文件
|
删除OBS文件
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ class OBS(Protocol):
|
|||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
def Link(self, obs_path: str) -> str:
|
def get_link(self, obs_path: str) -> str:
|
||||||
"""
|
"""
|
||||||
获取OBS文件链接
|
获取OBS文件链接
|
||||||
|
|
||||||
@@ -68,6 +70,31 @@ class OBS(Protocol):
|
|||||||
str: OBS文件链接
|
str: OBS文件链接
|
||||||
"""
|
"""
|
||||||
...
|
...
|
||||||
|
|
||||||
|
def delete_by_link(self, obs_link: str) -> error:
|
||||||
|
"""
|
||||||
|
根据OBS文件链接删除文件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obs_link (str): OBS文件链接
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: 是否删除成功
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
def get_obs_path_by_link(self, obs_link: str) -> (str, error):
|
||||||
|
"""
|
||||||
|
从OBS文件链接获取OBS路径
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obs_link (str): OBS文件链接
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: OBS文件路径
|
||||||
|
error: 错误信息
|
||||||
|
"""
|
||||||
|
...
|
||||||
|
|
||||||
|
|
||||||
class Koodo:
|
class Koodo:
|
||||||
@@ -82,7 +109,7 @@ class Koodo:
|
|||||||
self.bucket = qiniu.BucketManager(self.auth)
|
self.bucket = qiniu.BucketManager(self.auth)
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def Put(self, obs_path: str, content: bytes) -> str:
|
def put(self, obs_path: str, content: bytes) -> str:
|
||||||
"""
|
"""
|
||||||
上传文件到OBS
|
上传文件到OBS
|
||||||
|
|
||||||
@@ -103,7 +130,7 @@ class Koodo:
|
|||||||
logging.info(f"文件 {obs_path} 上传成功, OBS路径: {full_path}")
|
logging.info(f"文件 {obs_path} 上传成功, OBS路径: {full_path}")
|
||||||
return f"{self.outer_domain}/{full_path}"
|
return f"{self.outer_domain}/{full_path}"
|
||||||
|
|
||||||
def Get(self, obs_path: str) -> bytes:
|
def get(self, obs_path: str) -> bytes:
|
||||||
"""
|
"""
|
||||||
从OBS下载文件
|
从OBS下载文件
|
||||||
|
|
||||||
@@ -121,7 +148,7 @@ class Koodo:
|
|||||||
return None
|
return None
|
||||||
return resp.content
|
return resp.content
|
||||||
|
|
||||||
def List(self, prefix: str = "") -> list[str]:
|
def list(self, prefix: str = "") -> list[str]:
|
||||||
"""
|
"""
|
||||||
列出OBS目录下的所有文件
|
列出OBS目录下的所有文件
|
||||||
|
|
||||||
@@ -143,7 +170,7 @@ class Koodo:
|
|||||||
# logging.debug(f"info: {info}")
|
# logging.debug(f"info: {info}")
|
||||||
return keys
|
return keys
|
||||||
|
|
||||||
def Del(self, obs_path: str) -> bool:
|
def delete(self, obs_path: str) -> error:
|
||||||
"""
|
"""
|
||||||
删除OBS文件
|
删除OBS文件
|
||||||
|
|
||||||
@@ -151,17 +178,17 @@ class Koodo:
|
|||||||
obs_path (str): OBS文件路径
|
obs_path (str): OBS文件路径
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
bool: 是否删除成功
|
error: 删除结果
|
||||||
"""
|
"""
|
||||||
ret, info = self.bucket.delete(self.bucket_name, f"{self.prefix_path}{obs_path}")
|
ret, info = self.bucket.delete(self.bucket_name, f"{self.prefix_path}{obs_path}")
|
||||||
logging.debug(f"文件 {obs_path} 删除 OBS, 结果: {ret}, 状态码: {info.status_code}, 错误信息: {info.text_body}")
|
logging.debug(f"文件 {self.prefix_path}{obs_path} 删除 OBS, 结果: {ret}, 状态码: {info.status_code}, 错误信息: {info.text_body}")
|
||||||
if ret is None or info.status_code != 200:
|
if ret is None or info.status_code != 200:
|
||||||
logging.error(f"文件 {obs_path} 删除 OBS 失败, 错误信息: {info.text_body}")
|
logging.error(f"文件 {obs_path} 删除 OBS 失败, 错误信息: {info.text_body}")
|
||||||
return False
|
return error(error_code=ErrorCode.OBS_INPUT_ERROR, error_info=f"文件 {self.prefix_path}{obs_path} 删除 OBS 失败, 错误信息: {info.text_body}")
|
||||||
logging.info(f"文件 {obs_path} 删除 OBS 成功")
|
logging.info(f"文件 {obs_path} 删除 OBS 成功")
|
||||||
return True
|
return error(error_code=ErrorCode.SUCCESS, error_info="success")
|
||||||
|
|
||||||
def Link(self, obs_path: str) -> str:
|
def get_link(self, obs_path: str) -> str:
|
||||||
"""
|
"""
|
||||||
获取OBS文件链接
|
获取OBS文件链接
|
||||||
|
|
||||||
@@ -173,6 +200,38 @@ class Koodo:
|
|||||||
"""
|
"""
|
||||||
return f"{self.outer_domain}/{self.prefix_path}{obs_path}"
|
return f"{self.outer_domain}/{self.prefix_path}{obs_path}"
|
||||||
|
|
||||||
|
def delete_by_link(self, obs_link: str) -> error:
|
||||||
|
"""
|
||||||
|
根据OBS文件链接删除文件
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obs_link (str): OBS文件链接
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
error: 删除结果
|
||||||
|
"""
|
||||||
|
obs_path, err = self.get_obs_path_by_link(obs_link)
|
||||||
|
if not err.success:
|
||||||
|
return err
|
||||||
|
return self.delete(obs_path)
|
||||||
|
|
||||||
|
def get_obs_path_by_link(self, obs_link: str) -> (str, error):
|
||||||
|
"""
|
||||||
|
从OBS文件链接获取OBS路径
|
||||||
|
|
||||||
|
Args:
|
||||||
|
obs_link (str): OBS文件链接
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
str: OBS文件路径
|
||||||
|
error: 错误信息
|
||||||
|
"""
|
||||||
|
if not obs_link.startswith(f"{self.outer_domain}/{self.prefix_path}"):
|
||||||
|
logging.error(f"文件 {obs_link} 不是 OBS 文件链接")
|
||||||
|
return "", error(error_code=ErrorCode.OBS_INPUT_ERROR, error_info=f"文件 {obs_link} 不是 OBS 文件链接")
|
||||||
|
obs_path = obs_link[len(self.outer_domain) + len(self.prefix_path) + 1:]
|
||||||
|
return obs_path, error(error_code=ErrorCode.SUCCESS, error_info="success")
|
||||||
|
|
||||||
|
|
||||||
_obs_instance: OBS = None
|
_obs_instance: OBS = None
|
||||||
|
|
||||||
@@ -213,8 +272,8 @@ if __name__ == "__main__":
|
|||||||
# print(f"文件 {obs_path} 链接: {link}")
|
# print(f"文件 {obs_path} 链接: {link}")
|
||||||
|
|
||||||
# 列出OBS目录下的所有文件
|
# 列出OBS目录下的所有文件
|
||||||
keys = obs.List("")
|
keys = obs.list("")
|
||||||
print(f"OBS 目录下的所有文件: {keys}")
|
print(f"OBS 目录下的所有文件: {keys}")
|
||||||
for key in keys:
|
for key in keys:
|
||||||
link = obs.Del(key)
|
link = obs.delete(key)
|
||||||
print(f"文件 {key} 删除 OBS 成功: {link}")
|
print(f"文件 {key} 删除 OBS 成功: {link}")
|
||||||
|
|||||||
119
src/web/api.py
119
src/web/api.py
@@ -38,38 +38,52 @@ async def ping():
|
|||||||
class PostInputRequest(BaseModel):
|
class PostInputRequest(BaseModel):
|
||||||
text: str
|
text: str
|
||||||
|
|
||||||
@api.post("/api/recognition/input")
|
@authorized_router.post("/api/upload/image")
|
||||||
async def post_input(request: PostInputRequest):
|
async def post_upload_image(image: UploadFile = File(...)):
|
||||||
people = extract_people(request.text)
|
|
||||||
resp = BaseResponse(error_code=0, error_info="success")
|
|
||||||
resp.data = people.to_dict()
|
|
||||||
return resp
|
|
||||||
|
|
||||||
@api.post("/api/recognition/image")
|
|
||||||
async def post_input_image(image: UploadFile = File(...)):
|
|
||||||
# 实现上传图片的处理
|
# 实现上传图片的处理
|
||||||
# 保存上传的图片文件
|
# 保存上传的图片文件
|
||||||
# 生成唯一的文件名
|
# 生成唯一的文件名
|
||||||
file_extension = os.path.splitext(image.filename)[1]
|
file_extension = os.path.splitext(image.filename)[1]
|
||||||
unique_filename = f"{uuid.uuid4()}{file_extension}"
|
unique_filename = f"{uuid.uuid4()}{file_extension}"
|
||||||
|
|
||||||
# 确保uploads目录存在
|
|
||||||
os.makedirs("uploads", exist_ok=True)
|
|
||||||
|
|
||||||
# 保存文件到对象存储
|
# 保存文件到对象存储
|
||||||
file_path = f"uploads/{unique_filename}"
|
file_path = f"uploads/{unique_filename}"
|
||||||
obs_util = obs.get_instance()
|
obs_util = obs.get_instance()
|
||||||
obs_util.Put(file_path, await image.read())
|
obs_util.put(file_path, await image.read())
|
||||||
|
|
||||||
# 获取对象存储外链
|
# 获取对象存储外链
|
||||||
obs_url = obs_util.Link(file_path)
|
obs_url = obs_util.get_link(file_path)
|
||||||
|
return BaseResponse(error_code=0, error_info="success", data=obs_url)
|
||||||
|
|
||||||
|
@authorized_router.post("/api/recognition/input")
|
||||||
|
async def post_recognition_input(request: PostInputRequest):
|
||||||
|
people = extract_people(request.text)
|
||||||
|
resp = BaseResponse(error_code=0, error_info="success")
|
||||||
|
resp.data = people.to_dict()
|
||||||
|
return resp
|
||||||
|
|
||||||
|
@authorized_router.post("/api/recognition/image")
|
||||||
|
async def post_recognition_image(image: UploadFile = File(...)):
|
||||||
|
# 实现上传图片的处理
|
||||||
|
# 保存上传的图片文件
|
||||||
|
# 生成唯一的文件名
|
||||||
|
file_extension = os.path.splitext(image.filename)[1]
|
||||||
|
unique_filename = f"{uuid.uuid4()}{file_extension}"
|
||||||
|
|
||||||
|
# 保存文件到对象存储
|
||||||
|
file_path = f"uploads/{unique_filename}"
|
||||||
|
obs_util = obs.get_instance()
|
||||||
|
obs_util.put(file_path, await image.read())
|
||||||
|
|
||||||
|
# 获取对象存储外链
|
||||||
|
obs_url = obs_util.get_link(file_path)
|
||||||
logging.info(f"obs_url: {obs_url}")
|
logging.info(f"obs_url: {obs_url}")
|
||||||
|
|
||||||
# 调用OCR处理图片
|
# 调用OCR处理图片
|
||||||
ocr_util = ocr.get_instance()
|
ocr_util = ocr.get_instance()
|
||||||
ocr_result = ocr_util.recognize_image_text(obs_url)
|
ocr_result = ocr_util.recognize_image_text(obs_url)
|
||||||
logging.info(f"ocr_result: {ocr_result}")
|
logging.info(f"ocr_result: {ocr_result}")
|
||||||
|
|
||||||
people = extract_people(ocr_result, obs_url)
|
people = extract_people(ocr_result, obs_url)
|
||||||
resp = BaseResponse(error_code=0, error_info="success")
|
resp = BaseResponse(error_code=0, error_info="success")
|
||||||
resp.data = people.to_dict()
|
resp.data = people.to_dict()
|
||||||
@@ -130,7 +144,7 @@ class GetPeopleRequest(BaseModel):
|
|||||||
query: Optional[str] = None
|
query: Optional[str] = None
|
||||||
conds: Optional[dict] = None
|
conds: Optional[dict] = None
|
||||||
top_k: int = 5
|
top_k: int = 5
|
||||||
|
|
||||||
@authorized_router.get("/api/peoples")
|
@authorized_router.get("/api/peoples")
|
||||||
async def get_peoples(
|
async def get_peoples(
|
||||||
request: Request,
|
request: Request,
|
||||||
@@ -142,7 +156,7 @@ async def get_peoples(
|
|||||||
limit: int = Query(10, description="分页大小"),
|
limit: int = Query(10, description="分页大小"),
|
||||||
offset: int = Query(0, description="分页偏移量"),
|
offset: int = Query(0, description="分页偏移量"),
|
||||||
):
|
):
|
||||||
|
|
||||||
# 解析查询参数为字典
|
# 解析查询参数为字典
|
||||||
conds = {}
|
conds = {}
|
||||||
conds["user_id"] = getattr(request.state, 'user_id', '')
|
conds["user_id"] = getattr(request.state, 'user_id', '')
|
||||||
@@ -156,7 +170,7 @@ async def get_peoples(
|
|||||||
conds["height"] = height
|
conds["height"] = height
|
||||||
if marital_status:
|
if marital_status:
|
||||||
conds["marital_status"] = marital_status
|
conds["marital_status"] = marital_status
|
||||||
|
|
||||||
logging.info(f"conds: , limit: {limit}, offset: {offset}")
|
logging.info(f"conds: , limit: {limit}, offset: {offset}")
|
||||||
|
|
||||||
results = []
|
results = []
|
||||||
@@ -174,7 +188,7 @@ class RemarkRequest(BaseModel):
|
|||||||
|
|
||||||
|
|
||||||
@authorized_router.post("/api/people/{people_id}/remark")
|
@authorized_router.post("/api/people/{people_id}/remark")
|
||||||
async def post_remark(request: Request, people_id: str, body: RemarkRequest):
|
async def post_people_remark(request: Request, people_id: str, body: RemarkRequest):
|
||||||
service = get_people_service()
|
service = get_people_service()
|
||||||
res, err = service.get(people_id)
|
res, err = service.get(people_id)
|
||||||
if not err.success or not res:
|
if not err.success or not res:
|
||||||
@@ -188,7 +202,7 @@ async def post_remark(request: Request, people_id: str, body: RemarkRequest):
|
|||||||
|
|
||||||
|
|
||||||
@authorized_router.delete("/api/people/{people_id}/remark")
|
@authorized_router.delete("/api/people/{people_id}/remark")
|
||||||
async def delete_remark(request: Request, people_id: str):
|
async def delete_people_remark(request: Request, people_id: str):
|
||||||
service = get_people_service()
|
service = get_people_service()
|
||||||
res, err = service.get(people_id)
|
res, err = service.get(people_id)
|
||||||
if not err.success or not res:
|
if not err.success or not res:
|
||||||
@@ -201,6 +215,64 @@ async def delete_remark(request: Request, people_id: str):
|
|||||||
return BaseResponse(error_code=0, error_info="success")
|
return BaseResponse(error_code=0, error_info="success")
|
||||||
|
|
||||||
|
|
||||||
|
@authorized_router.post("/api/people/{people_id}/image")
|
||||||
|
async def post_people_image(request: Request, people_id: str, image: UploadFile = File(...)):
|
||||||
|
|
||||||
|
# 检查 people id 是否存在
|
||||||
|
service = get_people_service()
|
||||||
|
people, err = service.get(people_id)
|
||||||
|
if not err.success:
|
||||||
|
return BaseResponse(error_code=err.code, error_info=err.info)
|
||||||
|
|
||||||
|
if people.user_id != getattr(request.state, 'user_id', ''):
|
||||||
|
return BaseResponse(error_code=ErrorCode.MODEL_ERROR.value, error_info="permission denied")
|
||||||
|
|
||||||
|
# 实现上传图片的处理
|
||||||
|
# 保存上传的图片文件
|
||||||
|
# 生成唯一的文件名
|
||||||
|
file_extension = os.path.splitext(image.filename)[1]
|
||||||
|
unique_filename = f"{uuid.uuid4()}{file_extension}"
|
||||||
|
|
||||||
|
# 保存文件到对象存储
|
||||||
|
file_path = f"peoples/{people_id}/images/{unique_filename}"
|
||||||
|
obs_util = obs.get_instance()
|
||||||
|
obs_util.put(file_path, await image.read())
|
||||||
|
|
||||||
|
# 获取对象存储外链
|
||||||
|
obs_url = obs_util.get_link(file_path)
|
||||||
|
logging.info(f"obs_url: {obs_url}")
|
||||||
|
|
||||||
|
return BaseResponse(error_code=0, error_info="success", data=obs_url)
|
||||||
|
|
||||||
|
|
||||||
|
@authorized_router.delete("/api/people/{people_id}/image")
|
||||||
|
async def delete_people_image(request: Request, people_id: str, image_url: str):
|
||||||
|
# 检查 people id 是否存在
|
||||||
|
service = get_people_service()
|
||||||
|
people, err = service.get(people_id)
|
||||||
|
if not err.success:
|
||||||
|
return BaseResponse(error_code=err.code, error_info=err.info)
|
||||||
|
|
||||||
|
if people.user_id != getattr(request.state, 'user_id', ''):
|
||||||
|
return BaseResponse(error_code=ErrorCode.MODEL_ERROR.value, error_info="permission denied")
|
||||||
|
|
||||||
|
# 检查 image_url 是否是该 people 名下的图片链接
|
||||||
|
obs_util = obs.get_instance()
|
||||||
|
obs_path, err = obs_util.get_obs_path_by_link(image_url)
|
||||||
|
if not err.success:
|
||||||
|
return BaseResponse(error_code=err.code, error_info=err.info)
|
||||||
|
if not obs_path.startswith(f"peoples/{people_id}/images/"):
|
||||||
|
return BaseResponse(error_code=ErrorCode.OBS_INPUT_ERROR, error_info=f"文件 {image_url} 不是 {people_id} 名下的图片链接")
|
||||||
|
|
||||||
|
# 实现删除图片的处理
|
||||||
|
# 删除对象存储中的文件
|
||||||
|
err = obs_util.delete_by_link(image_url)
|
||||||
|
if not err.success:
|
||||||
|
return BaseResponse(error_code=err.code, error_info=err.info)
|
||||||
|
|
||||||
|
return BaseResponse(error_code=0, error_info="success")
|
||||||
|
|
||||||
|
|
||||||
class SendCodeRequest(BaseModel):
|
class SendCodeRequest(BaseModel):
|
||||||
target_type: str
|
target_type: str
|
||||||
target: str
|
target: str
|
||||||
@@ -411,4 +483,5 @@ async def update_user_email(request: Request, body: UpdateEmailRequest):
|
|||||||
}
|
}
|
||||||
return BaseResponse(error_code=0, error_info="success", data=data)
|
return BaseResponse(error_code=0, error_info="success", data=data)
|
||||||
|
|
||||||
|
|
||||||
api.include_router(authorized_router)
|
api.include_router(authorized_router)
|
||||||
|
|||||||
Reference in New Issue
Block a user