在作为脚本运行代码时调用外部API正常,但使用FastAPI运行同样代码时却收到`500 Internal Server Error`?
我有一个应用程序,可以预测图片中鱼的大小。我创建了一个FastAPI的接口,叫做/predict/
,这个接口会执行多个步骤来进行预测。这些步骤包括两次调用外部的API(这些API我无法控制,所以我只能看到它们返回的结果)。
当我直接从脚本运行代码,比如通过一个开发环境(我用的是PyCharm),预测的步骤都能正常运行,并且我从两个API那里得到了合适的响应。
第一个API是Roboflow,这是我运行脚本时的输出示例(我只是从命令行调用这个,或者在PyCharm中点击运行):
2024-03-30 10:59:36,073 - DEBUG - Starting new HTTPS connection (1): detect.roboflow.com:443
2024-03-30 10:59:36,339 - DEBUG - https://detect.roboflow.com:443 "POST /fish_measure/1?api_key=AY3KX4KMynZroEOyXUEb&disable_active_learning=False HTTP/1.1" 200 914
第二个API是Fishial,这是我运行脚本时的输出示例(无论是脚本还是通过PyCharm),这个API需要获取token、url等信息:
2024-03-30 11:02:31,866 - DEBUG - Starting new HTTPS connection (1): api-users.fishial.ai:443
2024-03-30 11:02:33,273 - DEBUG - https://api-users.fishial.ai:443 "POST /v1/auth/token HTTP/1.1" 200 174
2024-03-30 11:02:33,273 - INFO - Access token: eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE3MTE4MTE1NTMsImtpZCI6ImIzZjNiYWZlMTg2NGNjYmM3ZmFkNmE5YSJ9.YtlaecKMyxjipBDS97xNV3hYKcF3jRpOxTAVnwrxOcE
2024-03-30 11:02:33,273 - INFO - Obtaining upload url...
2024-03-30 11:02:33,582 - DEBUG - Starting new HTTPS connection (1): api.fishial.ai:443
2024-03-30 11:02:33,828 - DEBUG - https://api.fishial.ai:443 "POST /v1/recognition/upload HTTP/1.1" 200 1120
2024-03-30 11:02:33,829 - INFO - Uploading picture to the cloud...
2024-03-30 11:02:33,852 - DEBUG - Starting new HTTPS connection (1): storage.googleapis.com:443
2024-03-30 11:02:34,179 - DEBUG - https://storage.googleapis.com:443 "PUT /backend-fishes-storage-prod/6r9p24qp4llhat8mliso8xacdxm5?GoogleAccessId=services-storage-client%40ecstatic-baton-230905.iam.gserviceaccount.com&Expires=1711811253&Signature=gCGPID7bLuw%2FzUfv%2FLrTRPeQA060CaXQEqITPvW%2FWZ5GHXYKDRNCxVrUJ7UmpHVa0m60gIMFwFSQhYqsDmP3SkjI7ZnJSIEj53zxtOpcL7o2VGv6ZUuoowWwzmzqeM9yfbCHGI3TmtuW0lMhqAyi6Pc0wYhj73P12QU28wF8sdQMblHQLQVd1kFXtPl5yjSW12ADt4WEvB7dbnl7HmUTcL8WFS2SnJ1zcLljIbXTlRWcqc88MIcklSLG69z%2FJcUSh%2BeNxRp%2Fzotv5GitJBq9pF%2BzRt25lCt%2BYHGViJ46uu4rQapZBfACxsE762a1ZcrvTasy97idKRaijLJKAtZBRQ%3D%3D HTTP/1.1" 200 0
2024-03-30 11:02:34,180 - INFO - Requesting fish recognition...
2024-03-30 11:02:34,182 - DEBUG - Starting new HTTPS connection (1): api.fishial.ai:443
2024-03-30 11:02:39,316 - DEBUG - https://api.fishial.ai:443 "GET /v1/recognition/image?q=eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBMksyUEE9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--d37fdc2d5c6d8943a59dbd11326bc8a651f9bd69 HTTP/1.1" 200 10195
这是我为接口写的代码:
from fastapi import FastAPI, File, UploadFile, HTTPException, BackgroundTasks
from fastapi.middleware.cors import CORSMiddleware
from pydantic import BaseModel
from typing import Union
class PredictionResult(BaseModel):
prediction: Union[float, str]
eyeball_estimate: Union[float, str]
species: str
elapsed_time: float
@app.post("/predict/", response_model=PredictionResult)
async def predict_fish_length(file: UploadFile = File(...)):
try:
# capture the start of the process so we can track duration
start_time = time.time()
# Create a temporary file
temp_file = tempfile.NamedTemporaryFile(delete=False)
temp_file_path = temp_file.name
with open(temp_file_path, "wb") as buffer:
shutil.copyfileobj(file.file, buffer)
temp_file.close()
prediction = process_one_image(temp_file_path)
end_time = time.time() # Record the end time
elapsed_time = end_time - start_time # Calculate the elapsed time
return PredictionResult(
prediction=prediction["prediction"][0],
eyeball_estimate=prediction["eye_ratio_len_est"][0],
species=prediction["species"][0],
elapsed_time=elapsed_time
)
except Exception as e:
# Clean up the temp file in case of an error
os.unlink(temp_file_path)
raise HTTPException(status_code=500, detail=str(e)) from e
我通过uvicorn
来运行这个接口,然后尝试用curl
来调用接口,方法如下:
curl -X POST http://127.0.0.1:8000/predict/ -F "file=@/path/to/image.jpg"
Roboflow的API调用都没问题,但我从Fishial(第二个)API得到的响应是这样的:
2024-03-30 10:48:09,166 - DEBUG - Starting new HTTPS connection (1): api.fishial.ai:443
2024-03-30 10:48:10,558 - DEBUG - https://api.fishial.ai:443 "GET /v1/recognition/image?q=eyJfcmFpbHMiOnsibWVzc2FnZSI6IkJBaHBBMWkyUEE9PSIsImV4cCI6bnVsbCwicHVyIjoiYmxvYl9pZCJ9fQ==--36e68766cd891eb0e57610e8fb84b76e205b639e HTTP/1.1" 500 89
INFO: 127.0.0.1:49829 - "POST /predict/ HTTP/1.1" 500 Internal Server Error
我不太确定该去哪里查找,或者应该打印出什么信息来获取更多的细节。我甚至不确定这个错误是我这边的问题,还是我调用的API的问题(不过500 89
在GET请求的最后让我觉得问题可能出在我调用的API上)。
非常感谢!
编辑:有人请求更多的代码。处理图片的函数其实就是一系列调用其他函数的过程。所以我在这里只包含了我用来调用第二个(Fishial)API的代码:
def recognize_fish(file_path, key_id=key_id, key_secret=key_secret, identify=False):
if not os.path.isfile(file_path):
err("Invalid picture file path.")
for dep in DEPENDENCIES:
try:
__import__(dep)
except ImportError:
err(f"Unsatisfied dependency: {dep}")
logging.info("Identifying picture metadata...")
name = os.path.basename(file_path)
mime = mimetypes.guess_type(file_path)[0]
size = os.path.getsize(file_path)
with open(file_path, "rb") as f:
csum = base64.b64encode(hashlib.md5(f.read()).digest()).decode("utf-8")
logging.info(f"\n file name: {name}")
logging.info(f" MIME type: {mime}")
logging.info(f" byte size: {size}")
logging.info(f" checksum: {csum}\n")
if identify:
return
if not key_id or not key_secret:
err("Missing key ID or key secret.")
logging.info("Obtaining auth token...")
data = {
"client_id": key_id,
"client_secret": key_secret
}
response = requests.post("https://api-users.fishial.ai/v1/auth/token", json=data)
auth_token = response.json()["access_token"]
auth_header = f"Bearer {auth_token}"
logging.info(f"Access token: {auth_token}")
logging.info("Obtaining upload url...")
data = {
"blob": {
"filename": name,
"content_type": mime,
"byte_size": size,
"checksum": csum
}
}
headers = {
"Authorization": auth_header,
"Content-Type": "application/json",
"Accept": "application/json"
}
response = requests.post("https://api.fishial.ai/v1/recognition/upload", json=data, headers=headers)
signed_id = response.json()["signed-id"]
upload_url = response.json()["direct-upload"]["url"]
content_disposition = response.json()["direct-upload"]["headers"]["Content-Disposition"]
logging.info("Uploading picture to the cloud...")
with open(file_path, "rb") as f:
requests.put(upload_url, data=f, headers={
"Content-Disposition": content_disposition,
"Content-MD5": csum,
"Content-Type": ""
})
logging.info("Requesting fish recognition...")
response = requests.get(f"https://api.fishial.ai/v1/recognition/image?q={signed_id}",
headers={"Authorization": auth_header})
fish_count = len(response.json()["results"])
logging.info(f"Fishial Recognition found {fish_count} fish(es) on the picture.")
if fish_count == 0:
return []
species_names = []
for i in range(fish_count):
fish_data = extract_from_json(f"results[{i}]", response.json())
if fish_data and "species" in fish_data:
logging.info(f"Fish {i + 1} is:")
for j in range(len(fish_data["species"])):
species_data = fish_data["species"][j]
if "fishangler-data" in species_data and "metaTitleName" in species_data["fishangler-data"]:
species_name = species_data["fishangler-data"]["metaTitleName"]
accuracy = species_data["accuracy"]
logging.info(f" - {species_name} [accuracy {accuracy}]")
species_names.append(species_name)
else:
logging.error(" - Species name not found in the response.")
else:
logging.error(f"\nFish {i + 1}: Species data not found in the response.")
return species_names
附言:感觉这段内容有点长。如果把这么多代码放在Pastebin上更合适,我可以进行编辑。
2 个回答
你从Fishial API收到的错误信息显示是500内部服务器错误。这意味着问题可能出在服务器那边(Fishial API),而不是你的代码。
查看一下Fishial API的文档,确保你使用的接口和参数是正确的。
确认一下你在请求中传递的API密钥(如果需要的话)是正确的。