FastAPI的异步接口在处理完成后未删除临时音频文件
我有一个FastAPI语音端点,它会把上传的音频文件保存到磁盘,进行处理,然后在一个 finally 块中尝试删除它。但文件并没有被删除。
下面是我的代码:
@app.post("/voice")
async def voice(
telegramId: str = Form(...),
username: str = Form(""),
firstName: str = Form(""),
audio: UploadFile = File(...),
):
user = get_or_create_user(telegramId, username, firstName)
os.makedirs("uploads", exist_ok=True)
path = os.path.join("uploads", f"{uuid4().hex}.oga")
try:
with open(path, "wb") as f:
f.write(await audio.read())
result = voice_to_text(path)
if not native_text:
return {"reply": "Could not understand audio", "buttons": []}
finally:
if os.path.exists(path):
os.remove(path)
解决方案
导致你的文件无法删除的原因在于文件锁定,以及 finally 块在 FastAPI 中对早期返回的处理方式。
当你把路径传给 voice_to_text(path) 时,那库会打开文件来读取它。如果它没有明确地立即释放文件句柄,操作系统就会锁定它。当你的早期 return 触发时,Python会暂停响应,先执行 finally 块。由于请求仍在积极完成中,文件仍被锁定,os.remove() 会静默失败或被阻塞。
老实说我现在没心情把完整的代码块写出来,但解决办法很简单:使用FastAPI的 BackgroundTasks。
只需要把 background_tasks: BackgroundTasks 放进你的端点参数中,快速写一个类似 def remove_file(path): os.remove(path) 的辅助函数,并在保存完文件后就用 background_tasks.add_task(remove_file, path) 触发它。
如果有帮助,请告诉我!
编辑: 哦,等等,我刚意识到你也在完全泄漏FastAPI的内部临时文件。当有人上传一个 UploadFile 时,FastAPI会把它缓存到系统的 /tmp 文件夹里(如果大小超过某个阈值)。在你的代码里,你正试图删除自定义的 uploads/ 文件,但你从未调用 await audio.close()。那条流会一直保持打开,所以服务器的磁盘最终会堆满幽灵文件。确保你的后台任务在执行 await audio.close() 的同时,也执行 os.remove(),以便同时清理两者。