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

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

我有接受 JSON 数据和文件的 API (

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/

服务器日志显示,

内容类型是
图片/png

我可以看到

content_type
被自动猜测并且
image/png
已设置。

然后,我通过

requests
python
尝试了同样的事情。

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

控制台显示

内容类型是

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"
        }
    )

任何帮助表示赞赏。

python curl python-requests fastapi
1个回答
4
投票

首先,我建议查看这个答案,它提供了有关如何在 FastAPI 中上传文件的详细信息。其次,您似乎没有随文件一起发送

JSON
数据,而是发送
Form
数据。使用
data
函数的
requests.post()
参数时就是这种情况,无论在服务器端使用
metadata
定义
Body()
参数并在客户端使用
json.dumps()
来序列化该参数的值。

在客户端,您还需要将请求的

Content-Type
设置为
application/json
,或者仅使用
json
参数而不是
data
(请参阅 here,以及 herehere )。但是,由于您尝试同时发送文件和 JSON 数据,因此它不起作用 - 请查看这个答案,了解为什么您的方法不起作用,以及如何解决该问题。因此,您提供的示例之所以有效,只是因为您将
Form
数据与
File
一起发送,这对于
multipart/form-data
内容类型有效(另请参阅 here 了解如何使用 Python 请求发送
multipart/form-data
).

至于文件的

content_type
(在本例中更称为媒体类型,以前称为MIME类型,请参阅MDN的文档这里这里)未收到,这与
requests
模块。在 cURL 中,这是根据文件扩展名匹配来猜测的(请注意,有一个内置的 Python 模块,称为
mimetypes
,它提供类似的功能 - 这个答案的最后一部分提供了示例)。但是,在
requests
中,您可以在发送请求时手动为
content_type
设置
files
— 请参阅相关文档 herehere(查看
files
参数)了解更多详细信息,以及这个答案。如果您想避免这种情况,您可以使用
httpx
,它会自动为您设置
content_type
,并且仍然允许您以与
requests
类似的方式手动更改它,如果您愿意的话,如here所述(在 FastAPI 中使用
httpx
的相关帖子可能也有帮助,可以找到这里这里)。下面提供了这两个选项。

工作示例

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())
© www.soinside.com 2019 - 2024. All rights reserved.