如何使用 Graph API 将文件从 FastAPI 应用程序上传到 Facebook 页面?

问题描述 投票:0回答:1

当我尝试使用 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"
  }
}

有人知道如何解决这个问题吗?

python facebook-graph-api file-upload fastapi starlette
1个回答
1
投票

该错误是由于使用 Starlette 的

SpooledTemporaryFile
对象在幕后使用的
UploadFile
对象发送二进制数据而引起的 - 请查看 thisanswerthisanswer 了解更多详细信息和示例 - 无需指定
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,
        )

© www.soinside.com 2019 - 2024. All rights reserved.