Как отправлять файлы через HTTP
Что проверяют
Понимаете ли вы, что происходит на уровне протокола: чем multipart/form-data отличается от сырого тела и что будет с файлом в 2 ГБ. Следом спрашивают, как принять файл на сервере и как ограничить размер.
Как отвечать
Два способа передать файл:
multipart/form-data: тело делится на части разделителем (boundary), у каждой свои заголовки с именем поля, именем файла и типом. Так работают HTML-формы, и так файл уходит вместе с обычными полями;- сырое тело:
PUTилиPOSTсContent-Type: application/octet-streamили реальным типом файла, один файл на запрос. Проще и удобнее для потоковой передачи.
Клиент на Python:
- requests:
requests.post(url, files={"file": ("report.pdf", f, "application/pdf")}, timeout=30), файл открыт в режиме"rb". Multipart-телоrequestsсобирает в памяти целиком, для больших файлов берутMultipartEncoderизrequests-toolbeltили передают файл вdata=, тогда он уходит потоком; - httpx: тот же параметр
files=плюс асинхронный клиент, если сервис на asyncio.
Сервер: в FastAPI UploadFile (нужен пакет python-multipart, крупный файл сбрасывается во временный файл на диске), в Django request.FILES, файлы больше FILE_UPLOAD_MAX_MEMORY_SIZE (по умолчанию 2,5 МБ) тоже пишутся на диск. Перед приложением обычно nginx с client_max_body_size, по умолчанию 1 МБ, сверх него клиент получает 413.
Большие файлы лучше вообще не гнать через свой бэкенд: сервер выдаёт presigned URL на S3-совместимое хранилище, клиент грузит напрямую, при необходимости частями через multipart upload.
Где спотыкаются
Сами ставят заголовок Content-Type: multipart/form-data. В нём нет boundary, и сервер не может разобрать тело. Этот заголовок библиотека выставляет сама.
Кодируют файл в base64 и кладут в JSON. Объём растёт примерно на треть, и весь файл оказывается в памяти с обеих сторон.
Не ставят timeout. У requests его по умолчанию нет, и зависшая загрузка держит воркер бесконечно. Туда же относятся проверка типа по содержимому, а не по расширению, и очистка имени файла от ../.