Запит файлів¶
Ви можете визначити файли, які будуть завантажуватися клієнтом, використовуючи File.
Інформація
Щоб отримувати завантажені файли, спочатку встановіть python-multipart.
Переконайтеся, що ви створили віртуальне середовище, активували його, а потім встановили пакет, наприклад:
$ pip install python-multipart
Це необхідно, оскільки завантажені файли передаються у вигляді «form data».
Імпорт File¶
Імпортуйте File та UploadFile з fastapi:
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}
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
Визначення параметрів File¶
Створіть параметри файлів так само як ви б створювали Body або Form:
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}
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
Інформація
File — це клас, який безпосередньо успадковує Form.
Але пам’ятайте, що коли ви імпортуєте Query, Path, File та інші з fastapi, це насправді функції, які повертають спеціальні класи.
Порада
Щоб оголосити тіла файлів, вам потрібно використовувати File, тому що інакше параметри будуть інтерпретовані як параметри запиту або параметри тіла (JSON).
Файли будуть завантажені у вигляді «form data».
Якщо ви оголосите тип параметра функції операції шляху як bytes, FastAPI прочитає файл за вас, і ви отримаєте його вміст у вигляді bytes.
Майте на увазі, що це означає, що весь вміст буде збережено в пам'яті. Це працюватиме добре для малих файлів.
Але є кілька випадків, у яких вам може бути корисно використовувати UploadFile.
Параметри файлу з UploadFile¶
Визначте параметр файлу з типом UploadFile:
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}
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File()):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile):
return {"filename": file.filename}
Використання UploadFile має кілька переваг перед bytes:
- Вам не потрібно використовувати
File()у значенні за замовчуванням параметра. - Використовується «spooled» файл:
- Файл зберігається в пам'яті до досягнення максимального обмеження розміру, після чого він буде збережений на диску.
- Це означає, що він добре працюватиме для великих файлів, таких як зображення, відео, великі двійкові файли тощо, не споживаючи всю пам'ять.
- Ви можете отримати метадані про завантажений файл.
- Він має file-like
asyncінтерфейс. - Він надає фактичний об'єкт Python
SpooledTemporaryFile, який можна передавати безпосередньо іншим бібліотекам, що очікують file-like об'єкт.
UploadFile¶
UploadFile має такі атрибути:
filename: Рядокstrз оригінальною назвою файлу, який був завантажений (наприклад,myimage.jpg).content_type: Рядокstrз типом вмісту (MIME type / media type) (наприклад,image/jpeg).file:SpooledTemporaryFile(file-like об'єкт). Це фактичний файловий об'єкт Python, який ви можете передавати безпосередньо іншим функціям або бібліотекам, що очікують «file-like» об'єкт.
UploadFile має такі асинхронні async методи. Вони всі викликають відповідні методи файлу під капотом (використовуючи внутрішній SpooledTemporaryFile).
write(data): Записуєdata(strабоbytes) у файл.read(size): Читаєsize(int) байтів/символів з файлу.seek(offset): Переходить до байтової позиціїoffset(int) у файлі.- Наприклад,
await myfile.seek(0)перейде на початок файлу. - Це особливо корисно, якщо ви виконаєте
await myfile.read()один раз, а потім потрібно знову прочитати вміст.
- Наприклад,
close(): Закриває файл.
Оскільки всі ці методи є асинхронними async методами, вам потрібно їх «await»-ити.
Наприклад, всередині async функції операції шляху ви можете отримати вміст за допомогою:
contents = await myfile.read()
Якщо ви знаходитесь у звичайній def функції операції шляху, ви можете отримати доступ до UploadFile.file безпосередньо, наприклад:
contents = myfile.file.read()
Технічні деталі async
Коли ви використовуєте async методи, FastAPI виконує файлові методи у пулі потоків і очікує на них.
Технічні деталі Starlette
UploadFile у FastAPI успадковується безпосередньо від UploadFile у Starlette, але додає деякі необхідні частини, щоб зробити його сумісним із Pydantic та іншими частинами FastAPI.
Що таке «Form Data»¶
Спосіб, у який HTML-форми (<form></form>) надсилають дані на сервер, зазвичай використовує «спеціальне» кодування для цих даних, відмінне від JSON.
FastAPI забезпечить зчитування цих даних з правильного місця, а не з JSON.
Технічні деталі
Дані з форм зазвичай кодуються за допомогою «media type» application/x-www-form-urlencoded, якщо вони не містять файлів.
Але якщо форма містить файли, вона кодується як multipart/form-data. Якщо ви використовуєте File, FastAPI знатиме, що потрібно отримати файли з правильної частини тіла.
Якщо ви хочете дізнатися більше про ці типи кодування та формові поля, ознайомтеся з MDN web docs для POST.
Попередження
Ви можете оголосити кілька параметрів File і Form в операції шляху, але ви не можете одночасно оголошувати поля Body, які ви очікуєте отримати як JSON, оскільки запит матиме тіло, закодоване як multipart/form-data, а не application/json.
Це не обмеження FastAPI, а частина протоколу HTTP.
Необов’язкове завантаження файлу¶
Ви можете зробити файл необов’язковим, використовуючи стандартні анотації типів і встановивши значення за замовчуванням None:
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes | None, File()] = None):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
🤓 Other versions and variants
from typing import Annotated, Union
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[Union[bytes, None], File()] = None):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: Union[UploadFile, None] = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes | None = File(default=None)):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: UploadFile | None = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
Tip
Prefer to use the Annotated version if possible.
from typing import Union
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Union[bytes, None] = File(default=None)):
if not file:
return {"message": "No file sent"}
else:
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(file: Union[UploadFile, None] = None):
if not file:
return {"message": "No upload file sent"}
else:
return {"filename": file.filename}
UploadFile із додатковими метаданими¶
Ви також можете використовувати File() разом із UploadFile, наприклад, щоб встановити додаткові метадані:
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: Annotated[bytes, File(description="A file read as bytes")]):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(
file: Annotated[UploadFile, File(description="A file read as UploadFile")],
):
return {"filename": file.filename}
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, File, UploadFile
app = FastAPI()
@app.post("/files/")
async def create_file(file: bytes = File(description="A file read as bytes")):
return {"file_size": len(file)}
@app.post("/uploadfile/")
async def create_upload_file(
file: UploadFile = File(description="A file read as UploadFile"),
):
return {"filename": file.filename}
Завантаження кількох файлів¶
Можна завантажувати кілька файлів одночасно.
Вони будуть пов’язані з одним і тим самим «form field», який передається у вигляді «form data».
Щоб це реалізувати, потрібно оголосити список bytes або UploadFile:
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(files: Annotated[list[bytes], File()]):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(files: list[bytes] = File()):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(files: list[UploadFile]):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)
Ви отримаєте, як і було оголошено, list із bytes або UploadFile.
Технічні деталі
Ви також можете використати from starlette.responses import HTMLResponse.
FastAPI надає ті ж самі starlette.responses, що й fastapi.responses, просто для зручності для вас, розробника. Але більшість доступних відповідей надходять безпосередньо від Starlette.
Завантаження кількох файлів із додатковими метаданими¶
Так само як і раніше, ви можете використовувати File(), щоб встановити додаткові параметри навіть для UploadFile:
from typing import Annotated
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(
files: Annotated[list[bytes], File(description="Multiple files as bytes")],
):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(
files: Annotated[
list[UploadFile], File(description="Multiple files as UploadFile")
],
):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)
🤓 Other versions and variants
Tip
Prefer to use the Annotated version if possible.
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import HTMLResponse
app = FastAPI()
@app.post("/files/")
async def create_files(
files: list[bytes] = File(description="Multiple files as bytes"),
):
return {"file_sizes": [len(file) for file in files]}
@app.post("/uploadfiles/")
async def create_upload_files(
files: list[UploadFile] = File(description="Multiple files as UploadFile"),
):
return {"filenames": [file.filename for file in files]}
@app.get("/")
async def main():
content = """
<body>
<form action="/files/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
<form action="/uploadfiles/" enctype="multipart/form-data" method="post">
<input name="files" type="file" multiple>
<input type="submit">
</form>
</body>
"""
return HTMLResponse(content=content)
Підсумок¶
Використовуйте File, bytes та UploadFile, щоб оголошувати файли для завантаження в запиті, надіслані у вигляді form data.