|
from typing import ( |
|
Any, |
|
BinaryIO, |
|
Callable, |
|
Dict, |
|
Iterable, |
|
Optional, |
|
Type, |
|
TypeVar, |
|
cast, |
|
) |
|
|
|
from fastapi._compat import ( |
|
PYDANTIC_V2, |
|
CoreSchema, |
|
GetJsonSchemaHandler, |
|
JsonSchemaValue, |
|
with_info_plain_validator_function, |
|
) |
|
from starlette.datastructures import URL as URL |
|
from starlette.datastructures import Address as Address |
|
from starlette.datastructures import FormData as FormData |
|
from starlette.datastructures import Headers as Headers |
|
from starlette.datastructures import QueryParams as QueryParams |
|
from starlette.datastructures import State as State |
|
from starlette.datastructures import UploadFile as StarletteUploadFile |
|
from typing_extensions import Annotated, Doc |
|
|
|
|
|
class UploadFile(StarletteUploadFile): |
|
""" |
|
A file uploaded in a request. |
|
|
|
Define it as a *path operation function* (or dependency) parameter. |
|
|
|
If you are using a regular `def` function, you can use the `upload_file.file` |
|
attribute to access the raw standard Python file (blocking, not async), useful and |
|
needed for non-async code. |
|
|
|
Read more about it in the |
|
[FastAPI docs for Request Files](https://fastapi.tiangolo.com/tutorial/request-files/). |
|
|
|
## Example |
|
|
|
```python |
|
from typing import Annotated |
|
|
|
from fastapi import FastAPI, File, UploadFile |
|
|
|
app = FastAPI() |
|
|
|
|
|
@app.post("/files/") |
|
async def create_file(file: Annotated[bytes, File()]): |
|
return {"file_size": len(file)} |
|
|
|
|
|
@app.post("/uploadfile/") |
|
async def create_upload_file(file: UploadFile): |
|
return {"filename": file.filename} |
|
``` |
|
""" |
|
|
|
file: Annotated[ |
|
BinaryIO, |
|
Doc("The standard Python file object (non-async)."), |
|
] |
|
filename: Annotated[Optional[str], Doc("The original file name.")] |
|
size: Annotated[Optional[int], Doc("The size of the file in bytes.")] |
|
headers: Annotated[Headers, Doc("The headers of the request.")] |
|
content_type: Annotated[ |
|
Optional[str], Doc("The content type of the request, from the headers.") |
|
] |
|
|
|
async def write( |
|
self, |
|
data: Annotated[ |
|
bytes, |
|
Doc( |
|
""" |
|
The bytes to write to the file. |
|
""" |
|
), |
|
], |
|
) -> None: |
|
""" |
|
Write some bytes to the file. |
|
|
|
You normally wouldn't use this from a file you read in a request. |
|
|
|
To be awaitable, compatible with async, this is run in threadpool. |
|
""" |
|
return await super().write(data) |
|
|
|
async def read( |
|
self, |
|
size: Annotated[ |
|
int, |
|
Doc( |
|
""" |
|
The number of bytes to read from the file. |
|
""" |
|
), |
|
] = -1, |
|
) -> bytes: |
|
""" |
|
Read some bytes from the file. |
|
|
|
To be awaitable, compatible with async, this is run in threadpool. |
|
""" |
|
return await super().read(size) |
|
|
|
async def seek( |
|
self, |
|
offset: Annotated[ |
|
int, |
|
Doc( |
|
""" |
|
The position in bytes to seek to in the file. |
|
""" |
|
), |
|
], |
|
) -> None: |
|
""" |
|
Move to a position in the file. |
|
|
|
Any next read or write will be done from that position. |
|
|
|
To be awaitable, compatible with async, this is run in threadpool. |
|
""" |
|
return await super().seek(offset) |
|
|
|
async def close(self) -> None: |
|
""" |
|
Close the file. |
|
|
|
To be awaitable, compatible with async, this is run in threadpool. |
|
""" |
|
return await super().close() |
|
|
|
@classmethod |
|
def __get_validators__(cls: Type["UploadFile"]) -> Iterable[Callable[..., Any]]: |
|
yield cls.validate |
|
|
|
@classmethod |
|
def validate(cls: Type["UploadFile"], v: Any) -> Any: |
|
if not isinstance(v, StarletteUploadFile): |
|
raise ValueError(f"Expected UploadFile, received: {type(v)}") |
|
return v |
|
|
|
@classmethod |
|
def _validate(cls, __input_value: Any, _: Any) -> "UploadFile": |
|
if not isinstance(__input_value, StarletteUploadFile): |
|
raise ValueError(f"Expected UploadFile, received: {type(__input_value)}") |
|
return cast(UploadFile, __input_value) |
|
|
|
if not PYDANTIC_V2: |
|
|
|
@classmethod |
|
def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None: |
|
field_schema.update({"type": "string", "format": "binary"}) |
|
|
|
@classmethod |
|
def __get_pydantic_json_schema__( |
|
cls, core_schema: CoreSchema, handler: GetJsonSchemaHandler |
|
) -> JsonSchemaValue: |
|
return {"type": "string", "format": "binary"} |
|
|
|
@classmethod |
|
def __get_pydantic_core_schema__( |
|
cls, source: Type[Any], handler: Callable[[Any], CoreSchema] |
|
) -> CoreSchema: |
|
return with_info_plain_validator_function(cls._validate) |
|
|
|
|
|
class DefaultPlaceholder: |
|
""" |
|
You shouldn't use this class directly. |
|
|
|
It's used internally to recognize when a default value has been overwritten, even |
|
if the overridden default value was truthy. |
|
""" |
|
|
|
def __init__(self, value: Any): |
|
self.value = value |
|
|
|
def __bool__(self) -> bool: |
|
return bool(self.value) |
|
|
|
def __eq__(self, o: object) -> bool: |
|
return isinstance(o, DefaultPlaceholder) and o.value == self.value |
|
|
|
|
|
DefaultType = TypeVar("DefaultType") |
|
|
|
|
|
def Default(value: DefaultType) -> DefaultType: |
|
""" |
|
You shouldn't use this function directly. |
|
|
|
It's used internally to recognize when a default value has been overwritten, even |
|
if the overridden default value was truthy. |
|
""" |
|
return DefaultPlaceholder(value) |
|
|