如何将文件对象传递给 FastAPI 端点中的 HTTPX 请求

How to pass File object to HTTPX request in FastAPI endpoint

想法是从一个端点获取文件对象并将其发送到其他端点以使用它而不保存它。 让我们有这个示例代码:

import httpx
from fastapi import Request, UploadFile, File


app = FastAPI()
client = httpx.AsyncClient()

@app.post("/endpoint/")
async def foo(request: Request, file: UploadFile = File(...))
    urls = ["/some/other/endpoint", "/another/endpoint/"]
    for url in urls:
        response = await client.post(url) # here I need to send the file to the other endpoint 
    return {"bar": "baz"}


@app.post("/some/other/endpoint/")
async def baz(request: Request, file: UploadFile = File(...)): # and here to use it
     # Do something with the file object
     return {"file": file.filename}


@app.post("/another/endpoint/")
async def baz(request: Request, file: UploadFile = File(...)): # and here to use it too
     # Do something with the file object
     return {"file": file.content_type}

如前所述here我试过这样做:

data = {'file': file}
response = await client.post(url, data=data)

但是出错了

'{"detail":[{"loc":["body","file"],"msg":"Expected UploadFile, received: <class \'str\'>","type":"value_error"}]}'

卷曲请求示例:

curl -X 'POST' -F 'file=@somefile' someserver/endpoint/

httpx类似于requests使用files=....发送文件。

post(..., files={'file': file.file}, ...)

或使用文件名

post(..., files={'file': (file.filename, file.file)}, ...)

顺便说一句:

如果多次发送同一个文件,则可能需要在发送后将指针移至文件开头

file.file.seek(0)

await file.seek(0)

完整的工作代码

from fastapi import FastAPI, Request, UploadFile, File
import httpx

app = FastAPI()
client = httpx.AsyncClient()


@app.post("/endpoint/")
async def foo(request: Request, file: UploadFile = File(...)):
    print('/endpoint/')
    
    urls = ["/some/other/endpoint/", "/another/endpoint/"]
    
    results = []
    
    for url in urls:
        response = await client.post('http://localhost:8000' + url, files={'file': (file.filename, file.file)})
        #file.file.seek(0)  # move back at the beginning of file after sending to other URL
        await file.seek(0)  # move back at the beginning of file after sending to other URL
        results.append(response)
        
    results = [item.text for item in results]
    
    print('results:', results)
    
    return {"bar": "baz"}


@app.post("/some/other/endpoint/")
async def baz(request: Request, file: UploadFile = File(...)):
    print('/some/other/endpoint/')
    
    print('filename:', file.filename)
    print('content_type:', file.content_type)
    
    # Do something with the file object
    
    return {"file": file.filename}


@app.post("/another/endpoint/")
async def baz(request: Request, file: UploadFile = File(...)): 
    print('/another/endpoint/')
    
    print('filename:', file.filename)
    print('content_type:', file.content_type)
    
    # Do something with the file object
    
    return {"file": file.content_type}