前面几篇主要讲解的是fastapi在接收JSON数据时,该如何进行处理。但是在Web中存在表单和文件两种类型的数据传输情况。本篇中主要针对这两种数据类型进行讲解。
表单数据
要使用表单,需预先安装python-multipart包。
# 激活虚拟环境
cd env/Scripts
activate
# 安装python-multipart包
pip install python-multipart
使用方法
要获取表单数据,就是要在路径操作函数中定义响应的变量,且该变量的默认值为Form函数的返回值。
下面是后端代码:
from random import paretovariate
from fastapi import FastAPI
# 演示获取表单数据的步骤
# 第一步:导入Form函数
from fastapi import Form
# 第二步:创建一个FastAPI类的实例对象
app = FastAPI()
# 第三步:创建一个路径装饰器
@app.post("/register")
# 第四步:创建一个路径操作函数,声明Form表单变量
async def create_user(username: str = Form(), password: str = Form()):
return {
"result": "register successfully",
"username": username,
"password": password
}
# 第五步:运行服务器
if __name__ == "__main__":
import uvicorn
uvicorn.run(app="main:app", host="127.0.0.1", port=8080, reload=True)
下面是前端测试页面代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>register</title>
</head>
<body>
<h2>这是一个测试fastapi获取表单数据的页面</h2>
<!--action路径需要和后端定义的路径匹配!!!-->
<form action="http://127.0.0.1:8080/register" method="post">
<label for="name">用户名:</label><input type="text" name="username" id="name"><br>
<label for="pwd">密码:</label><input type="password" name="password" id="pwd"><br>
<button type="submit">注册</button>
</form>
</body>
</html>
下面是测试结果:
表单字段注意事项
根据HTTP协议规定一次请求体中不能具有两种编码格式。
所以在一个路径操作函数中可以声明多个Form参数,但不能同时声明要接收JSON的Body字段。这是因为Form字段的编码是application/x-www-form-urlencoded,而Body字段的编码是application/json。
文件数据
File函数方式
使用File函数方式和使用Form函数方式相同,在路径操作函数中将文件参数的默认值设定为File函数的返回值。
下面是后端的代码示例:
# 演示接收文件数据
# File方式,利用File来声明这个变量接收的是一个文件数据
from fastapi import File
@app.post("/file/file")
# 此处的...表示None,...表示省略第一个形参
# filename需要和前端的变量名一致
async def get_file(filename:bytes = File(...)):
return {"file_size":len(filename)}
下面是前端的代码示例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>这是用来演示fastapi接收文件数据的前端演示</h2>
<form action="http://127.0.0.1:8080/file/file" method="post" enctype="multipart/form-data">
<!--注意此处filename必须和后端文件参数变量名一致-->
<label for="file1"></label><input type="file" name="filename" id="file1">
<button type="submit">上传</button>
</form>
</body>
</html>
下面是演示结果:
UploadFile类方式
利用fastapi包中的UploadFile类,来声明变量的数据类型,以引入文件参数。
后端代码演示如下:
# 演示UploadFile类接收文件数据
# UploadFile方式,利用UploadFile来声明变量的类型
from fastapi import UploadFile
@app.post("/file/uploadfile")
async def get_uploadfile(file:UploadFile):
# filename:上传文件名字符串(str),例如, myimage.jpg;
# content_type:内容类型(MIME 类型 / 媒体类型)字符串(str),例如,image/jpeg;
return{
"filename":file.filename,
"content_type":file.content_type
}
前端代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>这是用来演示fastapi接收文件数据的前端演示</h2>
<!--form 中包含文件时编码格式应该是multipart/form-data-->
<form action="http://127.0.0.1:8080/file/uploadfile" method="post" enctype="multipart/form-data">
<label for="file">文件</label><input type="file" name="file" id="file">
<button type="submit">上传文件</button>
</form>
</body>
</html>
演示结果:
多文件数据获取
想获取多个文件数据,只需要将接收文件数据的变量类型声明为列表即可。
后端代码如下:
# 演示File和UploadFile接收多文件数据
# 接收多文件时需要将接收文件类型的参数,声明为包含文件的列表形式
from typing import List
@app.post("/files/file")
async def get_files(files:List[bytes]=File(...)):
return {
"file_number":len(files)
}
@app.post("/files/uploadfile")
async def get_files(files:List[UploadFile]):
filesname = [file.filename for file in files]
return { "filesname":filesname}
前端代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Files</title>
</head>
<body>
<h2>演示File的多文件数据引用</h2>
<form action="http://127.0.0.1:8080/files/file" method="post" enctype="multipart/form-data">
<label for="files">
文件名:
</label>
<input type="file" name="files" id="files" multiple>
<button type="submit">上传文件</button>
</form>
<h2>演示UploadFile的多文件数据引用</h2>
<form action="http://127.0.0.1:8080/files/uploadfile" method="post" enctype="multipart/form-data">
<label for="files">
文件名:
</label>
<input type="file" name="files" id="files" multiple>
<button type="submit">上传文件</button>
</form>
</body>
</html>
演示结果:
File和UploadFile方式的优缺点
方式 | 优点 | 缺点 |
File() | 将文件的所有内容都存储在内存里,适用于小型文件 | 不适合存储大文件 |
UploadFile | 使用spooled文件:存储在内存的文件超出最大上限时,fastapi会把文件存入磁盘。适合处理图像,视频,二进制等大型文件,不会占用所有内存。自带file-like async接口,可以像操作文件一样操作文件数据 | – |
表单和文件混合
有时候在一个表单中可能既包含表单数据又包含文件数据,这种情况fastapi也是支持的。只需声明各个数据所对应的数据类型即可。
需要注意的是在前端中对form的编码需设定为multipart/form-data。
后端示例代码
# 演示既接收Form数据又接收文件数据
@app.post("/mixdata")
async def get_mixdata(username=Form(),file:UploadFile=File()):
return {
"username":username,
"filename":file.filename
}
前端示例代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h2>这是用来演示fastapi接收混合表单数据的前端演示</h2>
<!--form 中包含文件时编码格式应该是multipart/form-data-->
<form action="http://127.0.0.1:8080/mixdata" method="post" enctype="multipart/form-data">
<label for="username">用户名:</label><input type="text" name="username" id="username"><br>
<label for="file">文件:</label><input type="file" name="file" id="file"><br>
<button type="submit">提交</button>
</form>
</body>
</html>
演示结果: