上传多个文件 UploadFiles FastAPI

uploading multiple files UploadFiles FastAPI

例子

这是我的代码:

from typing import  List
from fastapi import FastAPI, File, UploadFile
import asyncio
import concurrent.futures

app = FastAPI()
@app.post("/send_images")
async def update_item(
    files: List[UploadFile] = File(...),
):
    return {"res": len(files)}

然后我向该服务器发送请求(这些特定的 URL 仅作为示例):

import requests
import os 
import json
import numpy as np
import time
import io
import httpx
import asyncio
from datetime import datetime
import pandas as pd

from random import shuffle
import pandas as pd
import urllib
import io
from PIL import Image
from matplotlib import pyplot as plt
import concurrent.futures

urls = ['https://sun9-63.userapi.com/c638920/v638920705/1a54d/xSREwpakJD4.jpg',
 'https://sun9-28.userapi.com/c854024/v854024084/1160d8/LDMVHYgguAw.jpg',
 'https://sun9-54.userapi.com/c854220/v854220084/111f66/LdcbEpPR6tg.jpg',
 'https://sun9-40.userapi.com/c841420/v841420283/4c8bb/Mii6GSCrmpo.jpg',
 'https://sun6-16.userapi.com/CPQpllJ0KtaArvQKkPsHTZDCupqjRJ_8l07ejA/iyg2hRR_kM4.jpg',
 'https://sun9-1.userapi.com/c638920/v638920705/1a53b/SMta6Bv-k7s.jpg',
 'https://sun9-36.userapi.com/c857332/v857332580/56ad/rJCGKFw03FQ.jpg',
 'https://sun6-14.userapi.com/iPsfmW0ibE8RsMh0k2lUFdRxHZ4Q41yctB7L3A/ajJHY3WN6Xg.jpg',
 'https://sun9-28.userapi.com/c854324/v854324383/1c1dc3/UuFigBF7WDI.jpg',
 'https://sun6-16.userapi.com/UVXVAT-tYudG5_24FMaBWTB9vyW8daSrO2WPFQ/RMjv7JZvowA.jpg']

os.environ['NO_PROXY'] = '127.0.0.1'

async def request_get_4(list_urls):
    async with httpx.AsyncClient() as client:
        r = httpx.post("http://127.0.0.1:8001/send_images", files={f'num_{ind}': el for ind, el in enumerate(list_urls)})
        print(r.text)
        return r

async def request_get_3(url):
    async with httpx.AsyncClient() as client:
        return await client.get(url)
    
from collections import defaultdict

async def main():
    start = datetime.now()
    tasks = [asyncio.create_task(request_get_3(url)) for url in urls[0:10]]
    result = await asyncio.gather(*tasks)
    
    data_to_send = []
    for ind, resp in enumerate(result):
        if resp.status_code == 200:
            image_bytes = io.BytesIO(resp.content)
            image_bytes.seek(0)
            data_to_send.append(image_bytes)
        
    end = datetime.now()
    print(result)
    print(len(data_to_send))

    batch_size = 2
    batch_num = len(data_to_send) // batch_size
    tasks = [asyncio.create_task(request_get_4(data_to_send[i * batch_size: (i+1) * batch_size])) for i in range(batch_num)]
    result = await asyncio.gather(*tasks)
    
    left_data = data_to_send[batch_size*(batch_num):]
    print(len(left_data))
    print(result)

asyncio.run(main())

我正在尝试加载包含在 url 中的图像,然后形成它们的批次并将它们发送到 FastAPI 服务器。但它不起作用。 我收到以下错误:

{"detail":[{"loc":["body","files"],"msg":"field required","type":"value_error.missing"}]}

如何解决我的问题并能够通过 httpx 将多个文件发送到 FastAPI?

问题是 HTTPX 0.13.3 不支持多文件上传,因为这个参数需要字典,而字典不能有相同的键值。

我们可以使用这个 pull request https://github.com/encode/httpx/pull/1032/files 来解决这个问题(现在它也可以接受 List[Tuple[str, FileTypes]]])!

更新: 这个问题现在已经解决了!

更新2: 这里我展示了我如何使用 httpx 来处理不同的请求:

async def request_post_batch(fastapi_url: str, url_content_batch: List[BinaryIO]) -> httpx.Response:
    """
    Send batch to FastAPI server.
    """
    async with httpx.AsyncClient(timeout=httpx.Timeout(100.0)) as client:
        r = await client.post(
            fastapi_url,
            files=[('bytes_image', url_content) for url_content in url_content_batch]
        )
        return r


async def request_post_logs(logstash_url: str, logs: List[Dict]) -> httpx.Response:
    """
    Send logs to logstash
    """
    async with httpx.AsyncClient(timeout=httpx.Timeout(100.0)) as client:
        r = await client.post(
            logstash_url,
            json=logs
        )
        return r

此示例将在 files 字段下传递多个文件。

async with httpx.AsyncClient(timeout=httpx.Timeout(100.0)) as client:    
     response = await client.post(f"/api/v1/upload-files",
                files=[("files", ("image.png", b"{}", "image/png")), ("files", ("image2.png", b"{}", "image/png"))],
            )