Skip to content

Commit

Permalink
【警告】请wcy运行前仔细检查我的代码是否存在致命性错误,尤其是对数据库的操作。建议运行前对数据库进行备份。
Browse files Browse the repository at this point in the history
feat: Add config.py and b2sdk==1.19.0 to requirements.txt, and make changes to users_router.py and utils.py
  • Loading branch information
diyanqi committed Mar 6, 2024
1 parent 9d733d2 commit 34c17e0
Show file tree
Hide file tree
Showing 8 changed files with 354 additions and 41 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -475,3 +475,4 @@ aes_key.txt

settings.py
test.py
config.py
32 changes: 32 additions & 0 deletions config.py.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# config.py - 【图床】的所有配置,放在根目录下。
# 什么?你说和 settings.py 是重复?那就是重复了呗,我懒得改了。以后再说,能跑就行。

# 储存方案设定
STORAGE = "" # 储存方式;这样一来,xuehai_url 可忽略
XUEHAI_URL = "" # 学海OSS上传地址
KEYID = "" # backblaze 的 keyid
KEYNAME = "" # backblaze 的 keyname(好像没用)
APPKEY = "" # backblaze 的 appkey
BASEURL = "" # backblaze 的 baseurl
CFURL = "" # CloudFlare 代理 backblaze 的 baseurl

# 数据库设定
DB_PATH = "" # 数据库路径

# 图片参数设定
UPLOAD_FOLDER = "" # 图片上传后的缓存文件夹
PAGE_SIZE = 10 # 获取图片列表时,默认的每页图片数量
PAGE_NUM = 1 # 获取图片列表时,默认的页码
MAX_SIZE = 3 * 1024 * 1024 # 上传图片最大尺寸,否则压缩

# 图片处理拓展设定
CHECK_ENABLED = False # 图片审查是否启用
KEYWORDS_GENERATE_ENABLED = False # 是否开启关键词生成
HF_ENABLED = False # 图生文是否启用
HF_URL = "" # HF模型服务地址

# 服务设定
SERVERURL = "" # 服务器运行地址
SUPERADMINTOKEN = ""
host = "" # 服务监听地址
port = 6666 # 服务监听端口
79 changes: 40 additions & 39 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,39 +1,40 @@
annotated-types==0.6.0
anyio==4.2.0
bcrypt==4.1.2
certifi==2024.2.2
charset-normalizer==3.3.2
click==8.1.7
dnspython==2.6.0
docopt==0.6.2
ecdsa==0.18.0
fastapi==0.109.2
h11==0.14.0
httptools==0.6.1
idna==3.6
motor==3.3.2
pipreqs==0.4.13
pyasn1==0.5.1
pycryptodome==3.20.0
pydantic==2.6.1
pydantic_core==2.16.2
PyJWT==2.8.0
pymongo==4.6.1
python-dotenv==1.0.1
python-jose==3.3.0
python-multipart==0.0.9
PyYAML==6.0.1
requests==2.31.0
rsa==4.9
setuptools==69.0.3
six==1.16.0
sniffio==1.3.0
starlette==0.36.3
typing_extensions==4.9.0
urllib3==2.2.0
uvicorn==0.27.1
uvloop==0.19.0
watchfiles==0.21.0
websockets==12.0
wheel==0.42.0
yarg==0.1.9
annotated-types==0.6.0
anyio==4.2.0
bcrypt==4.1.2
certifi==2024.2.2
charset-normalizer==3.3.2
click==8.1.7
dnspython==2.6.0
docopt==0.6.2
ecdsa==0.18.0
fastapi==0.109.2
h11==0.14.0
httptools==0.6.1
idna==3.6
motor==3.3.2
pipreqs==0.4.13
pyasn1==0.5.1
pycryptodome==3.20.0
pydantic==2.6.1
pydantic_core==2.16.2
PyJWT==2.8.0
pymongo==4.6.1
python-dotenv==1.0.1
python-jose==3.3.0
python-multipart==0.0.9
PyYAML==6.0.1
requests==2.31.0
rsa==4.9
setuptools==69.0.3
six==1.16.0
sniffio==1.3.0
starlette==0.36.3
typing_extensions==4.9.0
urllib3==2.2.0
uvicorn==0.27.1
uvloop==0.19.0
watchfiles==0.21.0
websockets==12.0
wheel==0.42.0
yarg==0.1.9
b2sdk==1.19.0
102 changes: 101 additions & 1 deletion routers/users_router.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import time
from fastapi import UploadFile
from fastapi import APIRouter, HTTPException, Depends, Form, Request
from typing import List
from fastapi.responses import StreamingResponse

from pydantic import BaseModel
from util import image_process, image_storage
from util.calculate import calculate_time
from util.cases import kebab_case_to_camel_case
from utils import (
Expand All @@ -10,13 +14,16 @@
timestamp_change,
validate_object_id,
get_img_token,
randomString,
)
from datetime import datetime, timedelta
from jose import JWTError, jwt
from database import db
from bson import ObjectId
import settings
from util.cert import get_hashed_password_by_cert, validate_by_cert
import os
import config

router = APIRouter()

Expand Down Expand Up @@ -227,7 +234,7 @@ async def read_notifications(user_oid: str, user=Depends(get_current_user)):
}


@router.get("/{user_oid}/imgtoken")
@router.get("/{user_oid}/imgtoken") # deprecated
async def get_imgtoken(
user_oid: str,
user=Depends(get_current_user),
Expand All @@ -242,3 +249,96 @@ async def get_imgtoken(
"code": 200,
"data": token,
}

@router.put("/image")
async def upload_image(
request: Request,
user=Depends(get_current_user),
):
# 上传图片
form = await request.form()
image = form.get("image")
if not isinstance(image, UploadFile):
raise HTTPException(status_code=400, detail="No image file provided")
filename = randomString() + '.jpg'
path = os.path.join(config.UPLOAD_FOLDER, filename)
with open(path, 'wb') as buffer:
buffer.write(await image.read())
image_process.compress(path, path, config.MAX_SIZE)
fileId = image_storage.upload(path)
if not fileId:
raise HTTPException(status_code=500, detail="Image storage failed")
timestamp = int(time.time())
# 添加到用户信息
db.zvms.users.update_one(
{"_id": validate_object_id(user["id"])},
{"$push": {"images": {"fileId": fileId, "timestamp": timestamp}}},
)
# 清空缓存
if os.path.exists(path):
os.remove(path)
return {
"status": "ok",
"code": 200,
"data": fileId,
}

@router.get("/image/show/{fileId}")
async def show_image(
fileId: str,
user=Depends(get_current_user),
):
# 判断用户是否登录
if not user:
raise HTTPException(status_code=401, detail="Unauthorized")
# 获取图片
image = image_storage.getBBImage(fileId)
if image.status_code != 200:
raise HTTPException(status_code=image.status_code, detail=image.text)
else:
def generate():
for chunk in image.iter_content(chunk_size=1024):
yield chunk
return StreamingResponse(generate(), media_type="image/jpeg")

@router.get("/image/{user_oid}")
async def read_images(
user_oid: str,
user=Depends(get_current_user),
):
# 获取用户的图片列表
if user["id"] != user_oid and "admin" not in user["per"]:
raise HTTPException(status_code=403, detail="Permission denied")
user = await db.zvms.users.find_one({"_id": validate_object_id(user_oid)})
return {
"status": "ok",
"code": 200,
"data": user["images"],
}

@router.delete("/image/{fileId}")
async def delete_image(
fileId: str,
user=Depends(get_current_user),
):
# 删除图片
flag = False
user = await db.zvms.users.find_one({"_id": validate_object_id(user["id"])})
images = user["images"]
for image in images:
if image["fileId"] == fileId:
images.remove(image)
image_storage.remove(fileId)
flag = True
break
await db.zvms.users.update_one(
{"_id": validate_object_id(user["id"])},
{"$set": {"images": images}},
)
if flag:
return {
"status": "ok",
"code": 200,
}
else:
raise HTTPException(status_code=404, detail="Image not found")
51 changes: 51 additions & 0 deletions util/image_backblaze.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# backblaze 的上传 api 有点复杂(对于我来说),所以我专门另开了一个文件。

import os
import b2sdk.v2 as b2
import config
import time
from pathlib import Path
import requests

info = b2.InMemoryAccountInfo()
b2_api = b2.B2Api(info)
application_key_id = config.KEYID
application_key = config.APPKEY
b2_api.authorize_account("production", application_key_id, application_key)
bucket = b2_api.get_bucket_by_name("zvms4-imgbed")

def getRealname(filePath):
if(filePath.find("/")):
return filePath[filePath.find("/") + 1:]
else:
return filePath

def bbUpload(filePath):
file_name = filePath
local_file = Path(file_name).resolve()
metadata = {"createdAt": str(time.time())}
uploaded_file = bucket.upload_local_file(
local_file=local_file,
file_name=getRealname(filePath),
file_infos=metadata,
)
return uploaded_file.id_
# return (b2_api.get_download_url_for_fileid(uploaded_file.id_))

def bbDownload(fileid, authorization_token = b2_api.account_info.get_account_auth_token()):
headers = None
if authorization_token is not None:
headers = {
'Authorization': authorization_token
}
download_url = b2_api.get_download_url_for_fileid(fileid)
download_url = config.CFURL + download_url[download_url.find(".com/") + 4:]
response = requests.get(download_url, headers=headers)
return response

def bbDelete(fileid):
bucket.delete_file_version(fileid, getRealname(fileid))

if __name__ == '__main__':
res = bbDownload("4_zd8ad465646ca80c885dc0611_f114a3c2c5d61f98c_d20240209_m053417_c005_v0501010_t0021_u01707456857078")
print(res)
71 changes: 71 additions & 0 deletions util/image_process.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# 图片处理相关

import requests
import hashlib
from PIL import Image
import os
from io import BytesIO
import config

if config.HF_ENABLED:
from gradio_client import Client

if config.HF_ENABLED:
client = Client(config.HF_URL)

# 鉴定是否含有不良信息
def checkImg(imgPath):

url = "https://eolink.o.apispace.com/nrsh/imgcheck"
payload = "imageUrl=&BizType="
headers = {
"X-APISpace-Token": "",
"Authorization-Type": "apikey",
"Content-Type": "application/x-www-form-urlencoded"
}

response = requests.post(url, headers=headers, data=payload)

# print(response.text)
return True # 合法

def compress(imgPath, outputPath, maxSize = 1024 * 1024):
img = Image.open(imgPath)
img = img.convert('RGB')
imgSize = os.path.getsize(imgPath)
if imgSize <= maxSize:
img.save(outputPath)
return
imgByteArr = BytesIO()
img.save(imgByteArr, format='JPEG')
imgByteArr = imgByteArr.getvalue()
imgByteArrLen = len(imgByteArr)
while imgByteArrLen > maxSize:
img = img.resize((int(img.size[0] * 0.9), int(img.size[1] * 0.9)))
imgByteArr = BytesIO()
img.save(imgByteArr, format='JPEG')
imgByteArr = imgByteArr.getvalue()
imgByteArrLen = len(imgByteArr)
with open(outputPath, 'wb') as f:
f.write(imgByteArr)

def generateThumbnail(imgPath, outputPath, width = 200, height = 200):
img = Image.open(imgPath)
img.thumbnail((width, height))
img.save(outputPath)

def generateMD5(imgPath):
with open(imgPath, 'rb') as f:
md5 = hashlib.md5(f.read()).hexdigest()
return md5

if config.HF_ENABLED:
def generateKeywords(imgUrl):
result = client.predict(
imgUrl, # str representing input in 'Input Image' Image component
api_name="/predict"
)
return (result)

if __name__ == '__main__':
checkImg("./exampleImg/ikun.jpg")
Loading

0 comments on commit 34c17e0

Please sign in to comment.