我有接受 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"
}
)
任何帮助表示赞赏。
首先,我建议查看这个答案,它提供了有关如何在 FastAPI 中上传文件的详细信息。其次,您似乎没有随文件一起发送
JSON
数据,而是发送 Form
数据。使用 data
函数的 requests.post()
参数时就是这种情况,无论在服务器端使用 metadata
定义 Body()
参数并在客户端使用 json.dumps()
来序列化该参数的值。
在客户端,您还需要将请求的
Content-Type
设置为 application/json
,或者仅使用 json
参数而不是 data
(请参阅 here,以及 here 和 here )。但是,由于您尝试同时发送文件和 JSON 数据,因此它不起作用 - 请查看这个答案,了解为什么您的方法不起作用,以及如何解决该问题。因此,您提供的示例之所以有效,只是因为您将 Form
数据与 File
一起发送,这对于 multipart/form-data
内容类型有效(另请参阅 here 了解如何使用 Python 请求发送 multipart/form-data
).
至于文件的
content_type
(在本例中更称为媒体类型,以前称为MIME类型,请参阅MDN的文档这里和这里)未收到,这与requests
模块。在 cURL 中,这是根据文件扩展名匹配来猜测的(请注意,有一个内置的 Python 模块,称为 mimetypes
,它提供类似的功能 - 这个答案的最后一部分提供了示例)。但是,在 requests
中,您可以在发送请求时手动为 content_type
设置 files
— 请参阅相关文档 here 和 here(查看 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())