很多网站都支持上传文件,比如说:注册时上传头像;填写问卷时上传附件等等。
Form表单其实是前端HTML语言中的一个标签语言,用来向服务端上传普通数据和文件。
示例1:Form表单的基本使用(浏览器直接打开该文件即可渲染出form表单页面)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登录页面</title> </head> <body>
<h1>用户登录</h1> <form action="/login" method="post"> <p>姓名: <input type="text" name="username"></p> <p>密码:<input type="password" name="password"></p> <p><input type="submit" value="登录"></p> </form>
</body> </html>
|
补充:常见的input标签类型
1 2 3 4 5 6 7 8 9 10
| <input type="text"> <input type="password"> <input type="date"> <input type="submit"> <input type="button"> <input type="reset"> <input type="radio"> <input type="checkbox"> <input type="file"> <input type="hidden">
|
示例2:Form表单用来上传文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>上传头像</title> </head> <body>
<h1>上传头像</h1> <form action="/login" method="post" enctype="multipart/form-data"> <p>姓名: <input type="text" name="username"></p> <p>密码:<input type="password" name="password"></p> <p>头像:<input type="file" name="avatar"></p> <p><input type="submit" value="提交"></p> </form>
</body> </html>
|
Form表单提交数据有两种请求方式,GET和POST。
当使用POST请求时,请求体数据有两种常用的编码方式,非常重要。
当请求体编码是application/x-www-form-urlencoded,此时编码方式和url中的查询参数编码格式一致,不能用来上传文件
当请求体编码是multipart/form-data时,既可以上传文件,也可以用来发送普通数据。
请求体数据的编码格式除了可以JOSN,还可以是使用form表单的application/x-www-form-urlencoded和multipart/form-data
当前端使用form表单提交数据,enctype指定application/x-www-form-urlencoded,此时FastAPI该怎么解析数据呢?
示例1:使用 Form()
函数来接收urlencoded编码格式的表单数据
1 2 3 4 5 6 7 8 9 10 11
| from fastapi import FastAPI, Form
app = FastAPI()
@app.post("/login/") def login(username: str = Form(), password: str = Form()): return { "username": username, "password": password }
|
路径函数中的参数如果希望是表单字段,必须使用 Form()
,否则会被当做查询参数和请求体参数。
Form()
和前面介绍的 Path()
、Query()
、Body()
、Header()
的用法基本一致,支持类型校核和转换。
想要使用 Form
接收表单数据,需要安装python扩展包:pip3 install python-multipart
除了使用postman也可以使用FastAPI自带的api文档请求接口
6-4 使用File上传文件
【前提】:需要提前下载第三方包:pip3 install python-multipart
示例1:使用 File()
用来接收表单发送过来的文件
- 路径函数中形参
file
要和表单中文件字段保持一致,且形参的类型为 bytes
,值为File()
,这样就可以直接将文件中二进制数据保存到形参file
中,也就是直接将文件内容读取到内存中,适合小文件上传。
1 2 3 4 5 6 7 8 9 10 11 12 13
| from fastapi import FastAPI, File
app = FastAPI()
@app.post("/upload") def upload_file(file: bytes = File()): print(file) with open("a.txt", "wb") as f: f.write(file) return file
|
使用api文档发送请求,也可以使用postman发送表单请求
示例2:形参file
的类型不能是str
,但如果没有类型,直接使用 file=File()
也可以,只不过这样需要特殊处理才能获取文件内容
1 2 3 4 5 6 7 8 9 10 11
| from fastapi import FastAPI, File
app = FastAPI()
@app.post("/upload") async def upload_file(file=File()): content = await file.read() print(content) return content
|
【重要】 对于上文件的上传需求,推荐使用示例1的方式。
【总结】只要在路径操作函数中声明了变量的类型是bytes
且使用了File,
则fastapi会将上传文件的内容全部去读到参数中。
6-5 使用UploadFile上传文件
前提】:需要提前下载第三方包:pip3 install python-multipart
【需求】:对于大文件,不适合直接把文件内容一次性读取到内存中,此时推荐使用 UploadFile
示例1:使用UploadFile类型的三个属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/upload") def upload_file(file: UploadFile): content = file.file.read() file.file.close() return { "filename": file.filename, "type": file.content_type, "content": content }
|
示例2:使用UploadFile类型的四个方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| from fastapi import FastAPI, UploadFile
app = FastAPI()
@app.post("/upload") async def upload_file(file: UploadFile): content = await file.read() await file.close() return { "filename": file.filename, "type": file.content_type, "content": content }
|
示例3:使用UploadFile的同时也可以是使用File
1 2 3 4 5 6 7 8 9 10 11 12
| from fastapi import FastAPI, UploadFile, File
app = FastAPI()
@app.post("/upload") def upload_file(file: UploadFile = File()):
return { "filename": file.filename, "type": file.content_type, }
|
【总结】
- UploadFile不会把文件内容全部加载到内存中,而是批量读取一定量的数据,边读边存硬盘,适合大文件。
- UploadFile类型问变量有3个属性(filename、content_type、file),4个异步方法(read\write\seek\close)。
6-6 设置上传文件是可选的
示例:设置默认值是None就表示上传文件是可选的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| from typing import Optional
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/file") def create_file(file: Optional[bytes] = File(default=None)): if not file: return {"message": "No file sent"} else: return {"file_size": len(file)}
@app.post("/uploadfile") def create_upload_file(file: Optional[UploadFile] = None): if not file: return {"message": "No upload file sent"} else: return {"filename": file.filename}
|
6-7 上传多个文件
示例:上传多个文件,文件字段名可以同名也可以不同名
- 同名字段时,也可使用
files: List[bytes] = File()
- 不同名字段,可以直接定义多个形参即可。
- 多文件上传,也可以使用UploadFile;也可以同时使用File和UploadFile
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| from typing import List
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files") def create_files(files: List[bytes] = File()): return {"file_sizes": [len(file) for file in files]}
@app.post("/files2") def create_files(file1: bytes = File(), file2: bytes = File()): return { "file1_size": len(file1), "file2_size": len(file2), }
@app.post("/uploadfiles") def create_upload_files(files: List[UploadFile]): return {"filenames": [file.filename for file in files]}
@app.post("/uploadfile2") def upload_file_and_file(file1: UploadFile, file2: bytes = File()): return { "file1_name": file1.filename, "file2_size": len(file2) }
|
6-8 同时接收表单数据和文件
- 很多时候我们需要同时接收表单数据和上传的文件数据,此时我们可以同时使用Form和File或UploadFile
示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| import typing from fastapi import FastAPI, Form, File, UploadFile
app = FastAPI()
@app.post("/register") def register( avatar: typing.Optional[bytes] = File(default=None), username: str = Form(), password: str = Form(min_length=6, max_length=10), accessary: typing.Optional[UploadFile] = None ): return { "username": username, "password": password, "avatar": len(avatar) if avatar else 0, "accessary": accessary.filename if accessary else "" }
|
【注意】:同时使用Form和File时,File要放在Form前面。