# Загрузка файлов { #request-files }

Используя класс `File`, мы можем позволить клиентам загружать файлы.

/// info | Дополнительная информация

Чтобы получать загруженные файлы, сначала установите <a href="https://github.com/Kludex/python-multipart" class="external-link" target="_blank">`python-multipart`</a>.

Убедитесь, что вы создали [виртуальное окружение](../virtual-environments.md){.internal-link target=_blank}, активировали его, а затем установили пакет, например:

```console
$ pip install python-multipart
```

Это связано с тем, что загружаемые файлы передаются как "данные формы".

///

## Импорт `File` { #import-file }

Импортируйте `File` и `UploadFile` из модуля `fastapi`:

{* ../../docs_src/request_files/tutorial001_an_py39.py hl[3] *}

## Определите параметры `File` { #define-file-parameters }

Создайте параметры `File` так же, как вы это делаете для `Body` или `Form`:

{* ../../docs_src/request_files/tutorial001_an_py39.py hl[9] *}

/// info | Дополнительная информация

`File` - это класс, который наследуется непосредственно от `Form`.

Но помните, что когда вы импортируете `Query`, `Path`, `File` и другие из `fastapi`, на самом деле это функции, которые возвращают специальные классы.

///

/// tip | Подсказка

Для объявления тела файла необходимо использовать `File`, поскольку в противном случае параметры будут интерпретироваться как параметры запроса или параметры тела (JSON).

///

Файлы будут загружены как данные формы.

Если вы объявите тип параметра у *функции операции пути* как `bytes`, то **FastAPI** прочитает файл за вас, и вы получите его содержимое в виде `bytes`.

Следует иметь в виду, что все содержимое будет храниться в памяти. Это хорошо подходит для небольших файлов.

Однако возможны случаи, когда использование `UploadFile` может оказаться полезным.

## Параметры файла с `UploadFile` { #file-parameters-with-uploadfile }

Определите параметр файла с типом `UploadFile`:

{* ../../docs_src/request_files/tutorial001_an_py39.py hl[14] *}

Использование `UploadFile` имеет ряд преимуществ перед `bytes`:

* Использовать `File()` в значении параметра по умолчанию не обязательно.
* При этом используется "буферный" файл:
    * Файл, хранящийся в памяти до максимального предела размера, после преодоления которого он будет храниться на диске.
* Это означает, что он будет хорошо работать с большими файлами, такими как изображения, видео, большие бинарные файлы и т.д., не потребляя при этом всю память.
* Из загруженного файла можно получить метаданные.
* Он реализует <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> `async` интерфейс.
* Он предоставляет реальный объект Python <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> который вы можете передать непосредственно другим библиотекам, которые ожидают файл в качестве объекта.

### `UploadFile` { #uploadfile }

`UploadFile` имеет следующие атрибуты:

* `filename`: Строка `str` с исходным именем файла, который был загружен (например, `myimage.jpg`).
* `content_type`: Строка `str` с типом содержимого (MIME type / media type) (например, `image/jpeg`).
* `file`: <a href="https://docs.python.org/3/library/tempfile.html#tempfile.SpooledTemporaryFile" class="external-link" target="_blank">`SpooledTemporaryFile`</a> (a <a href="https://docs.python.org/3/glossary.html#term-file-like-object" class="external-link" target="_blank">file-like</a> объект). Это фактический файл Python, который можно передавать непосредственно другим функциям или библиотекам, ожидающим файл в качестве объекта.

`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` *функции операции пути* можно получить содержимое с помощью:

```Python
contents = await myfile.read()
```

Если вы находитесь внутри обычной `def` *функции операции пути*, можно получить прямой доступ к файлу `UploadFile.file`, например:

```Python
contents = myfile.file.read()
```


/// note | Технические детали `async`

При использовании методов `async` **FastAPI** запускает файловые методы в пуле потоков и ожидает их.

///

/// note | Технические детали Starlette

**FastAPI** наследует `UploadFile` непосредственно из **Starlette**, но добавляет некоторые детали для совместимости с **Pydantic** и другими частями FastAPI.

///

## Что такое «данные формы» { #what-is-form-data }

Способ, которым HTML-формы (`<form></form>`) отправляют данные на сервер, обычно использует "специальную" кодировку для этих данных, отличную от JSON.

**FastAPI** позаботится о том, чтобы считать эти данные из нужного места, а не из JSON.

/// note | Технические детали

Данные из форм обычно кодируются с использованием "media type" `application/x-www-form-urlencoded` когда он не включает файлы.

Но когда форма включает файлы, она кодируется как multipart/form-data. Если вы используете `File`, **FastAPI** будет знать, что ему нужно получить файлы из нужной части тела.

Если вы хотите узнать больше об этих кодировках и полях форм, перейдите по ссылке <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/POST" class="external-link" target="_blank"><abbr title="Mozilla Developer Network – Сеть разработчиков Mozilla">MDN</abbr> web docs for <code>POST</code></a>.

///

/// warning | Внимание

В операции *функции операции пути* можно объявить несколько параметров `File` и `Form`, но нельзя также объявлять поля `Body`, которые предполагается получить в виде JSON, поскольку тело запроса будет закодировано с помощью `multipart/form-data`, а не `application/json`.

Это не является ограничением **FastAPI**, это часть протокола HTTP.

///

## Необязательная загрузка файлов { #optional-file-upload }

Вы можете сделать загрузку файла необязательной, используя стандартные аннотации типов и установив значение по умолчанию `None`:

{* ../../docs_src/request_files/tutorial001_02_an_py310.py hl[9,17] *}

## `UploadFile` с дополнительными метаданными { #uploadfile-with-additional-metadata }

Вы также можете использовать `File()` вместе с `UploadFile`, например, для установки дополнительных метаданных:

{* ../../docs_src/request_files/tutorial001_03_an_py39.py hl[9,15] *}

## Загрузка нескольких файлов { #multiple-file-uploads }

Можно одновременно загружать несколько файлов.

Они будут связаны с одним и тем же "полем формы", отправляемым с помощью данных формы.

Для этого необходимо объявить список `bytes` или `UploadFile`:

{* ../../docs_src/request_files/tutorial002_an_py39.py hl[10,15] *}

Вы получите, как и было объявлено, список `list` из `bytes` или `UploadFile`.

/// note | Технические детали

Можно также использовать `from starlette.responses import HTMLResponse`.

**FastAPI** предоставляет тот же `starlette.responses`, что и `fastapi.responses`, просто для удобства разработчика. Однако большинство доступных ответов поступает непосредственно из Starlette.

///

### Загрузка нескольких файлов с дополнительными метаданными { #multiple-file-uploads-with-additional-metadata }

Так же, как и раньше, вы можете использовать `File()` для задания дополнительных параметров, даже для `UploadFile`:

{* ../../docs_src/request_files/tutorial003_an_py39.py hl[11,18:20] *}

## Резюме { #recap }

Используйте `File`, `bytes` и `UploadFile` для работы с файлами, которые будут загружаться и передаваться в виде данных формы.
