使用cURL向FastAPI后端发送文件时的内容类型与Python请求不同

3pmvbmvn  于 4个月前  发布在  Python
关注(0)|答案(1)|浏览(108)

我有API,它接受JSON数据和文件(png)。
这是我的fastapi代码:

@app.post("/api/")
async def dummy_file_return(metadata=Body(...),file=File(...)):
  print("content_type is")
  print(file.content_type)

字符串
使用curl访问时。

$curl -X POST -F file=@0_for_django_testcase.png -F metadata='{"meta":"test"}' localhost:8008/api/


服务器日志显示,
content_type是
image/png
我可以看到content_type是自动猜测的,image/png是设置的。
然后,我用pythonrequests做了同样的事情。

response = requests.post(
        url,
        data={"metadata":json.dumps({"meta":"test")},
        files = {
            "file": open('0_for_django_testcase.png','rb')
        },
    )


控制台显示
content_type是
content_type是空的,什么也没有出现。
为什么会出现这种差异?

无论哪种方式,文件上传都成功,但content_type不同。

我没有为curl设置任何头,-F标志偷偷发送一些头?
在另一个试验中,我用头测试了这些模式,但都返回<Response [400]>错误。

response = requests.post(
        url,
        data={"metadata":json.dumps({"meta":"test")},
        files = {
            "file": open('0_for_django_testcase.png','rb')
        },
        headers={
            "Content-Type":"image/png"
        }
    )

    response = requests.post(
        url,
        data={"metadata":json.dumps({"meta":"test")},
        files = {
            "file": open('0_for_django_testcase.png','rb')
        },
        headers={
            "Content-Type":"multipart/form-data"
        }
    )


任何帮助感激不尽。

yb3bgrhw

yb3bgrhw1#

首先,我建议看看this answer,它提供了如何在FastAPI中上传文件的详细信息。其次,您似乎没有沿着发送JSON数据,而是发送Form数据。在requests中使用data参数时就是这种情况,无论在FastAPI端点中使用Body()定义参数,以及使用json.dumps()序列化metadata键的值。您还需要将请求的Content-Type设置为application/json,或者直接使用json参数(参见here,以及herehere)。然而,由于您尝试同时发送文件和JSON数据,因此它不起作用-请查看this answer,了解为什么您的方法不起作用,以及如何实现。因此,您提供的示例之所以有效,是因为您将Form数据沿着发送,这对于multipart/form-data内容类型是有效的(有关如何使用Python请求发送multipart/form-data,请参阅here)。
至于文件的content_type(在本例中更好地称为 media type,以前称为 MIME type,参见MDN的文档herehere),这与requests模块有关。(注意,有一个内置的Python模块,称为mimetypes,它提供了类似的功能-示例在this answer的最后一节中提供)。然而,在requests中,您可以在发送请求时手动为files设置content_type-请参阅此处和此处的相关文档(查看files参数)了解更多细节,以及this answer。如果您想避免这种情况,可以使用httpx,它会自动为您设置content_type,如果您愿意,仍然允许您以类似于requests的方式手动更改它,如本文所述(在FastAPI中使用httpx的相关文章可能也有帮助,可以在herehere中找到)。下面提供了这两个选项。

工作示例

app.py

from fastapi import FastAPI, File, UploadFile

app = FastAPI()

@app.post("/upload")
def upload(file: UploadFile = File(...)):
    try:
        print(f'Content type of file: {file.content_type}')
        contents = file.file.read()
        with open(file.filename, 'wb') as f:
            f.write(contents)
    except Exception:
        return {"message": "There was an error uploading the file"}
    finally:
        file.file.close()

    return {"message": f"Successfully uploaded {file.filename}"}

字符串

test.py(使用requests

import requests

url = 'http://127.0.0.1:8000/upload'
file = {'file': ('1.png', open('images/1.png', 'rb'), 'image/png')}
r = requests.post(url=url, files=file) 
print(r.json())

test.py(使用httpx

import httpx

url = 'http://127.0.0.1:8000/upload'
file = {'file': open('images/1.png', 'rb')}
r = httpx.post(url=url, files=file) 
print(r.json())

相关问题