当我尝试使用 python 中的 Graph API 和此函数将视频文件上传到 Facebook 页面时:
def upload_video_file(page_id: str, access_token: str, video_file: UploadFile):
upload_url = f"https://graph.facebook.com/{page_id}/videos"
headers = {"Authorization": f"Bearer {access_token}"}
files = {"file": video_file.file}
response = requests.post(upload_url, headers=headers, files=files)
data = response.json()
if data:
return data
else:
return {"message": "failed uploud video"}
并从以下 FastAPI 端点中执行上述函数:
@router.post("/upload-video/{page_id}")
async def post_video(page_id: str, video_file: UploadFile = File(...), access_token: str = Header(..., description="Access Token")):
response = upload_video_file(page_id, access_token, video_file)
return JSONResponse (response)pe here
我收到此错误:
{
"error": {
"message": "The video file you selected is in a format that we don't support.",
"type": "OAuthException",
"code": 352,
"error_subcode": 1363024,
"is_transient": false,
"error_user_title": "Format Video Tidak Didukung",
"error_user_msg": "Format video yang Anda coba unggah tidak didukung. Silakan coba lagi dengan sebuah video dalam format yang didukung.",
"fbtrace_id": "AZNNyQhyPDfi5AhDOBpdA5c"
}
}
有人知道如何解决这个问题吗?
该错误是由于使用 Starlette 的
SpooledTemporaryFile
对象在幕后使用的 UploadFile
对象发送二进制数据而引起的 - 请查看 thisanswer 和 thisanswer 了解更多详细信息和示例 - 无需指定filename
和/或 content-type
。
因此,解决方案是在发送 HTTP 请求之前定义
files
变量时指定这两个属性。您可以在 requests
和 here 找到相关的 files
文档(请参阅 files = {'file': ('video.mp4', video_file.file, 'video/mp4')}
参数)。您可能会发现这个答案也很有帮助。示例:
None
或者,如果您想使用用户上传的文件附带的内容,您可以使用以下内容(确保它们不是
files = {'file': (video_file.filename, video_file.file, video_file.content_type)}
):
requests
顺便说一句,我不建议使用
async
库在 requests
环境(例如 FastAPI)中执行 HTTP 请求。如果您仍然想使用 async
,您至少应该从端点中删除 await
定义,这将导致 FastAPI 在外部线程池中运行该端点,然后将其 async def
化,以防止阻止事件循环(以及整个服务器)的终点。请查看这个答案,了解有关 FastAPI 中的 def
和普通 httpx
的完整解释、详细信息和示例。
async
库,它也提供 requests
API,并且与 filename
具有非常相似的语法。详细信息和示例可以在这里、这里和这里找到。有关如何显式设置 content-type
和 files
的相关文档,可以在 here找到。不仅如此,您还可以在启动时初始化全局
Client
对象并在应用程序中重用它,而不是每次请求到达该端点时都创建一个新的 Client
会话。最后,当文件由于某种原因无法上传时,您还可以通过指定自定义响应
JSONResponse
,向用户返回自定义 status_code
- 有关更多详细信息,请参阅此答案。工作示例
from fastapi import FastAPI, Request, File, UploadFile, Header, status
from fastapi.responses import JSONResponse
from contextlib import asynccontextmanager
import httpx
@asynccontextmanager
async def lifespan(app: FastAPI):
# Initialise the Client on startup and add it to the state
async with httpx.AsyncClient() as client:
yield {'client': client}
# The Client closes on shutdown
app = FastAPI(lifespan=lifespan)
@app.post('/upload-video/{page_id}')
async def upload_video(
request: Request,
page_id: str,
file: UploadFile = File(...),
access_token: str = Header(...),
):
client = request.state.client
url = f'https://graph.facebook.com/{page_id}/videos'
files = {'file': (file.filename, file.file, file.content_type)}
headers = {'Authorization': f'Bearer {access_token}'}
req = client.build_request(method='POST', url=url, files=files, headers=headers)
r = await client.send(req)
if r.status_code == 200:
return r.json()
else:
return JSONResponse(
content='File failed to upload',
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
)