如何使用Fastapi上传文件?先从请求体数据讲起

简介: 如何使用Fastapi上传文件?先从请求体数据讲起

1、请求体数据

前面我们讲到,get请求中,我们将请求数据放在url中,其实是非常不安全的,我们更愿意将请求数据放在请求体中。


当你需要将数据从客户端(例如浏览器)发送给 API 时,你将其作为「请求体」发送。请求体是客户端发送给 API 的数据。响应体是 API 发送给客户端的数据。


FastAPI 基于 Pydantic ,Pydantic 主要用来做类型强制检查(校验数据)。不符合类型要求就会抛出异常。


对于 API 服务,支持类型检查非常有用,会让服务更加健壮,也会加快开发速度,因为开发者再也不用自己写一行一行的做类型检查。

安装上手

pip install pydantic
from fastapi import FastAPI  # FastAPI 是一个为你的 API 提供了所有功能的 Python 类。
import uvicorn

from typing import Optional,Union,List

from pydantic import BaseModel,Field

from datetime import date

#创建应用程序,app是应用程序名
app = FastAPI()  # 这个实例将是创建你所有 API 的主要交互对象。这个 app 同样在如下命令中被 uvicorn 所引用


#fastapi要实现校验功能,需要借助pydantic这个模块,我们需要自己写个类,继承pydantic模块中的BaseModel,才能具有该功能
#在类型上做类型限制
class User(BaseModel):
    name:str = 'root'
    #默认是0,输入限制大于0,小于100
    age: int = Field(default=0, lt=100, gt=0)
    birth: Optional[date] = None
    #限制为数组,里面的元素限制为int类型
    friends: List[int] = []
    description: Union[str, None] = None




#异步的请求参数,函数加上async
@app.post("/data") #路径参数与查询参数共存
#传参data限制为User类型
async def data(data:User):
    #将查询结果返回
    return {}




if __name__ == '__main__':
    #注意,run的第一个参数 必须是文件名:应用程序名
    uvicorn.run("请求体数据:app", port=8080,  reload=True)

在docs测试,可以看到请求体限制数据类型

报错排查

报错:


TypeError: Failed to execute ‘fetch’ on ‘Window’: Request with GET/HEAD method cannot have body.


有@ResponseBody才会在接口中获取swagger列表


是由于方法中申明的是get方法却用了@requestBody故将get 请求改为post 请求即可

当传参不符合限制要求,响应失败,提示年龄应小于100

当请求体参数完全符合要求,才能正确响应

我们可以将数据返回

#fastapi要实现校验功能,需要借助pydantic这个模块,我们需要自己写个类,继承pydantic模块中的BaseModel,才能具有该功能

#在类型上做类型限制
class User(BaseModel):
    name:str = 'root'
    #默认是0,输入限制大于0,小于100
    age: int = Field(default=0, lt=100, gt=0)
    birth: Optional[date] = None
    #限制为数组,里面的元素限制为int类型
    friends: List[int] = []
    description: Union[str, None] = None




#异步的请求参数,函数加上async
@app.post("/data") #路径参数与查询参数共存
#将传参data限制为User类型
async def data(data:User):
    print(data,type(data))
    #将查询结果返回
    return data

注意,当输入的数据类型跟限制类型不一致时,pydantic会尝试做数据类型转换,转换成功就可以正常返回,转换失败才报错


Field比较强大,可以做各种限制,甚至可以做正则限制 pattern

def Field(  # noqa: C901
    default: Any = PydanticUndefined,
    *,
    default_factory: typing.Callable[[], Any] | None = _Unset,
    alias: str | None = _Unset,
    alias_priority: int | None = _Unset,
    validation_alias: str | AliasPath | AliasChoices | None = _Unset,
    serialization_alias: str | None = _Unset,
    title: str | None = _Unset,
    description: str | None = _Unset,
    examples: list[Any] | None = _Unset,
    exclude: bool | None = _Unset,
    discriminator: str | types.Discriminator | None = _Unset,
    json_schema_extra: JsonDict | typing.Callable[[JsonDict], None] | None = _Unset,
    frozen: bool | None = _Unset,
    validate_default: bool | None = _Unset,
    repr: bool = _Unset,
    init: bool | None = _Unset,
    init_var: bool | None = _Unset,
    kw_only: bool | None = _Unset,
    pattern: str | None = _Unset,
    strict: bool | None = _Unset,
    gt: float | None = _Unset,
    ge: float | None = _Unset,
    lt: float | None = _Unset,
    le: float | None = _Unset,
    multiple_of: float | None = _Unset,
    allow_inf_nan: bool | None = _Unset,
    max_digits: int | None = _Unset,
    decimal_places: int | None = _Unset,
    min_length: int | None = _Unset,
    max_length: int | None = _Unset,
    union_mode: Literal['smart', 'left_to_right'] = _Unset,
    **extra: Unpack[_EmptyKwargs],


也可以自定义一个函数做限制,使用到了pydantic里面的validator装饰器

最新版的validator已被废弃

最新版使用field_validator装饰器

#在类型上做类型限制
class User(BaseModel):
    name:str = 'root'
    #默认是0,输入限制大于0,小于100
    age: int = Field(default=0, lt=100, gt=0)
    birth: Optional[date] = None
    #限制为数组,里面的元素限制为int类型
    friends: List[int] = []
    description: Union[str, None] = None

    @field_validator('name')
    def validate_name(cls,v):
        assert v.isalpha(), 'name must be alpha'
        return v

校验生效

类型嵌套:

我们定义的类型,可以组合嵌套方式使用

class Data(BaseModel):  # 类型嵌套
    users: List[User]


@app.post("/data/")
async def create_data(data: Data):
    # 添加数据库
    return data

也可以这样嵌套,请求体数据是列表套字典形式

2、form表单数据

在 OAuth2 规范的一种使用方式(密码流)中,需要将用户名、密码作为表单字段发送,而不是 JSON。

FastAPI 可以使用Form组件来接收表单数据,需要先使用 pip install python-multipart 命令进行安装。

pip install python-multipart

from fastapi import FastAPI, Form
import uvicorn


app = FastAPI()

@app.post("/regin")
def regin(username: str = Form(..., max_length=16, min_length=8, pattern='[a-zA-Z]'),   #Form对输入的数据可以做些限制
          password: str = Form(..., max_length=16, min_length=8, pattern='[0-9]')):
    print(f"username:{username},password:{password}")
    return {"username": username}




if __name__ == '__main__':
    #注意,run的第一个参数 必须是文件名:应用程序名
    uvicorn.run("表单:app", port=8080,  reload=True)

此时发送请求,content-type 必须是application/x-www-form-urlencoded

否则发送请求失败



使用application/x-www-form-urlencoded发送成功


3、小文件上传

文件上传,文件会放在请求体里面,但是请求头的content-type是multipart/form-data

1.单文件上传

# file: bytes = File():适合小文件上传
@app.post("/files/")
#文件时字节流类型,是fastapi里面的File类型
async def create_file(file: bytes = File()):
    print("file:", file)
    return {"file_size": len(file)}


在docs请求测试,可以看到请求的content-type是multipart/form-data

返回了图片的字节流长度

看下后台打印

但是这样上传只适合小文件,因为上传的文件会占用用户内存,太大的话会把内存撑爆

2.多文件上传

#多文件上传
@app.post("/multiFiles/")
async def create_files(files: List[bytes] = File()):
    for file in files:
        print(len(file))
    return {"file_sizes": [len(file) for file in files]}

点一次Add string item,就会增加一个文件上传按钮

看下后台打印

4、大文件上传

文件比较大时,如果一次性上传,可能会把用户内存撑爆,因此比较常见的处理方式就是分批上传。

上传大文件使用fastapi的UploadFile


1.单文件上传

from fastapi import FastAPI, File, UploadFile

# file: UploadFile:适合大文件上传,比较常用

@app.post("/uploadFile/")
#直接对应UploadFile类型数据
async def create_upload_file(file: UploadFile):
    #打印文件名称
    print('file',file.filename)
    #将上传的文件保存到服务本地
    with open(f"{file.filename}", 'wb') as f:
        #一次读取1024字节,循环读取写入
        for chunk in iter(lambda: file.file.read(1024), b''):
            f.write(chunk)

    return {"filename": file.filename}

后台打印

可以看到上传的文件被保存在服务端本地

单文件上传完整代码:

from fastapi import FastAPI, File, UploadFile
from typing import List
import uvicorn

app = FastAPI()

# file: UploadFile:适合大文件上传,比较常用

@app.post("/uploadFile/")
#直接对应UploadFile类型数据
async def create_upload_file(file: UploadFile):
    #打印文件名称
    print('file',file.filename)
    #将上传的文件保存到服务本地
    with open(f"{file.filename}", 'wb') as f:
        #一次读取1024字节,循环读取写入
        for chunk in iter(lambda: file.file.read(1024), b''):
            f.write(chunk)

    return {"filename": file.filename}


if __name__ == '__main__':
    #注意,run的第一个参数 必须是文件名:应用程序名
    uvicorn.run("文件上传:app", port=8080,  reload=True)

2.多文件上传

#上传多个文件
@app.post("/multiUploadFiles/")
async def create_upload_files(files: List[UploadFile]):
    for file in files:
        print(file.filename)
        # 将上传的文件保存到服务本地
        path = os.path.join('images',f'{file.filename}')
        with open(path, 'wb') as f:
            # 一次读取1024字节,循环读取写入
            for chunk in iter(lambda: file.file.read(1024), b''):
                f.write(chunk)

    return {"filenames": [file.filename for file in files]}


看下后台打印,以及上传的文件

查看下载的文件

多文件上传代码:

from fastapi import FastAPI, File, UploadFile
from typing import List
import uvicorn
import os

app = FastAPI()


#上传多个文件
@app.post("/multiUploadFiles/")
async def create_upload_files(files: List[UploadFile]):
    for file in files:
        print(file.filename)
        # 将上传的文件保存到服务本地
        path = os.path.join('images',f'{file.filename}')
        with open(path, 'wb') as f:
            # 一次读取1024字节,循环读取写入
            for chunk in iter(lambda: file.file.read(1024), b''):
                f.write(chunk)

    return {"filenames": [file.filename for file in files]}



if __name__ == '__main__':
    #注意,run的第一个参数 必须是文件名:应用程序名
    uvicorn.run("文件上传:app", port=8080,  reload=True)

总结:

怎么样小伙伴,使用fastapi实现文件上传是不是很简单,有兴趣抓紧试试吧!

相关文章
|
存储 API Python
FastAPI(24)- 详解 File,上传文件
FastAPI(24)- 详解 File,上传文件
1062 0
FastAPI(24)- 详解 File,上传文件
|
JSON IDE API
FastAPI(8)- 请求体 Request Body (下)
FastAPI(8)- 请求体 Request Body (下)
263 0
FastAPI(8)- 请求体 Request Body (下)
|
XML JSON JavaScript
FastApi-06-请求体-3
FastApi-06-请求体-3
131 0
|
JSON API 数据格式
FastAPI(8)- 请求体 Request Body (上)
FastAPI(8)- 请求体 Request Body (上)
629 0
FastAPI(8)- 请求体 Request Body (上)
|
存储 开发框架 JSON
FastAPI 学习之路(二十一)请求体 - 更新数据
FastAPI 学习之路(二十一)请求体 - 更新数据
FastAPI 学习之路(二十一)请求体 - 更新数据
|
开发框架 Python
FastAPI 学习之路(十七)上传文件
FastAPI 学习之路(十七)上传文件
FastAPI 学习之路(十七)上传文件
|
开发框架
FastAPI 学习之路(十一)请求体 - 嵌套模型
FastAPI 学习之路(十一)请求体 - 嵌套模型
FastAPI 学习之路(十一)请求体 - 嵌套模型
|
JSON 数据格式
FastAPI 学习之路(十)请求体的字段(下)
FastAPI 学习之路(十)请求体的字段(下)
FastAPI 学习之路(十)请求体的字段(下)