← Назад к вопросам

Как выглядит POST запрос передачи файла?

1.0 Junior🔥 221 комментариев
#REST API и HTTP

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

POST запрос передачи файла

POST запрос для передачи файлов использует специальный формат multipart/form-data, который позволяет передавать бинарные данные вместе с другой информацией на сервер.

Структура multipart/form-data запроса

Сырой HTTP запрос

POST /upload HTTP/1.1
Host: api.example.com
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary
Content-Length: 1024

------WebKitFormBoundary
Content-Disposition: form-data; name="file"; filename="photo.jpg"
Content-Type: image/jpeg

[бинарные данные файла...]
------WebKitFormBoundary
Content-Disposition: form-data; name="description"

My photo
------WebKitFormBoundary--

Компоненты запроса

  1. Content-Type: multipart/form-data — указывает тип контента
  2. boundary — разделитель между частями данных
  3. Content-Disposition — описание каждого поля
  4. Content-Type (для файла) — тип файла (MIME type)
  5. Бинарные данные — содержимое файла

Отправка файла с Python (requests)

Простой вариант: один файл

import requests

with open("photo.jpg", "rb") as f:
    files = {"file": f}
    response = requests.post("https://api.example.com/upload", files=files)
    print(response.json())

Расширенный вариант: файл + дополнительные данные

import requests
from pathlib import Path

file_path = Path("photo.jpg")

# Способ 1: открыть файл вручную
with open(file_path, "rb") as f:
    files = {"file": ("photo.jpg", f, "image/jpeg")}
    data = {"description": "My photo", "user_id": 123}
    response = requests.post(
        "https://api.example.com/upload",
        files=files,
        data=data
    )
    print(response.json())

# Способ 2: автоматическое определение типа
response = requests.post(
    "https://api.example.com/upload",
    files={"file": open(file_path, "rb")},
    data={"description": "My photo"}
)

Несколько файлов одновременно

import requests
from pathlib import Path

# Несколько файлов одного поля
files = {
    "files": [
        ("photo1.jpg", open("photo1.jpg", "rb"), "image/jpeg"),
        ("photo2.jpg", open("photo2.jpg", "rb"), "image/jpeg"),
        ("photo3.jpg", open("photo3.jpg", "rb"), "image/jpeg"),
    ]
}

response = requests.post(
    "https://api.example.com/upload-multiple",
    files=files
)

Отправка файла с использованием httpx (асинхронный вариант)

import httpx
import asyncio

async def upload_file():
    async with httpx.AsyncClient() as client:
        with open("photo.jpg", "rb") as f:
            files = {"file": ("photo.jpg", f, "image/jpeg")}
            response = await client.post(
                "https://api.example.com/upload",
                files=files
            )
            return response.json()

result = asyncio.run(upload_file())
print(result)

Обработка на стороне FastAPI сервера

Получение одного файла

from fastapi import FastAPI, File, UploadFile
from fastapi.responses import JSONResponse

app = FastAPI()

@app.post("/upload")
async def upload_file(file: UploadFile = File(...)):
    # UploadFile автоматически распаковывает multipart данные
    contents = await file.read()
    
    # Сохранение файла
    with open(f"uploads/{file.filename}", "wb") as f:
        f.write(contents)
    
    return {
        "filename": file.filename,
        "content_type": file.content_type,
        "size": len(contents)
    }

Получение нескольких файлов

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

@app.post("/upload-multiple")
async def upload_multiple(files: List[UploadFile] = File(...)):
    results = []
    
    for file in files:
        contents = await file.read()
        results.append({
            "filename": file.filename,
            "size": len(contents),
            "content_type": file.content_type
        })
    
    return {"uploaded": results}

Файл + дополнительные данные

from fastapi import FastAPI, File, UploadFile, Form
from pydantic import BaseModel

@app.post("/upload-with-metadata")
async def upload_with_metadata(
    file: UploadFile = File(...),
    description: str = Form(...),
    user_id: int = Form(...)
):
    contents = await file.read()
    
    return {
        "filename": file.filename,
        "description": description,
        "user_id": user_id,
        "size": len(contents)
    }

Детальный класс для работы с файлами

from pathlib import Path
from typing import Optional, List
import requests
import mimetypes

class FileUploader:
    def __init__(self, api_url: str):
        self.api_url = api_url
    
    def upload_single(
        self,
        file_path: str,
        metadata: Optional[dict] = None
    ) -> dict:
        """Загрузить один файл"""
        path = Path(file_path)
        
        if not path.exists():
            raise FileNotFoundError(f"File not found: {file_path}")
        
        mime_type, _ = mimetypes.guess_type(file_path)
        
        with open(file_path, "rb") as f:
            files = {"file": (path.name, f, mime_type)}
            response = requests.post(
                f"{self.api_url}/upload",
                files=files,
                data=metadata or {}
            )
            return response.json()
    
    def upload_multiple(
        self,
        file_paths: List[str],
        metadata: Optional[dict] = None
    ) -> dict:
        """Загрузить несколько файлов"""
        files = []
        
        for file_path in file_paths:
            path = Path(file_path)
            mime_type, _ = mimetypes.guess_type(file_path)
            
            with open(file_path, "rb") as f:
                files.append((
                    "files",
                    (path.name, f.read(), mime_type)
                ))
        
        response = requests.post(
            f"{self.api_url}/upload-multiple",
            files=files,
            data=metadata or {}
        )
        return response.json()
    
    def upload_with_validation(
        self,
        file_path: str,
        allowed_types: List[str],
        max_size_mb: int = 10
    ) -> dict:
        """Загрузить файл с проверкой"""
        path = Path(file_path)
        
        # Проверка существования
        if not path.exists():
            raise FileNotFoundError(f"File not found: {file_path}")
        
        # Проверка размера
        size_mb = path.stat().st_size / (1024 * 1024)
        if size_mb > max_size_mb:
            raise ValueError(f"File too large: {size_mb}MB > {max_size_mb}MB")
        
        # Проверка типа
        mime_type, _ = mimetypes.guess_type(file_path)
        if mime_type not in allowed_types:
            raise ValueError(f"Invalid file type: {mime_type}")
        
        with open(file_path, "rb") as f:
            files = {"file": (path.name, f, mime_type)}
            response = requests.post(
                f"{self.api_url}/upload",
                files=files
            )
            return response.json()

# Использование
uploader = FileUploader("https://api.example.com")

# Простая загрузка
result = uploader.upload_single(
    "photo.jpg",
    metadata={"description": "My photo"}
)

# Загрузка с проверкой
result = uploader.upload_with_validation(
    "document.pdf",
    allowed_types=["application/pdf"],
    max_size_mb=50
)

Практические примеры MIME типов

# Изображения
"image/jpeg"   # .jpg, .jpeg
"image/png"    # .png
"image/gif"    # .gif
"image/webp"   # .webp

# Документы
"application/pdf"              # .pdf
"application/msword"           # .doc
"application/vnd.openxmlformats-officedocument.wordprocessingml.document"  # .docx
"text/plain"                   # .txt

# Архивы
"application/zip"              # .zip
"application/x-rar-compressed" # .rar
"application/gzip"             # .gz

# Видео
"video/mp4"                    # .mp4
"video/mpeg"                   # .mpeg
"video/quicktime"              # .mov

Отправка больших файлов (streaming)

import requests

def upload_large_file(file_path: str, chunk_size: int = 1024 * 1024):
    """Загрузить большой файл по частям"""
    with open(file_path, "rb") as f:
        # requests автоматически stream-ит файлы
        response = requests.post(
            "https://api.example.com/upload",
            data=f  # Передаёшь file object напрямую
        )
        return response.json()

# Или явно с headers
with open("large_file.zip", "rb") as f:
    response = requests.post(
        "https://api.example.com/upload",
        data=f,
        headers={"Content-Type": "application/octet-stream"}
    )

Заключение

POST запрос для файлов использует формат multipart/form-data:

  • Content-Type: multipart/form-data с boundary разделителем
  • Каждая часть имеет Content-Disposition и Content-Type
  • Python requests автоматически форматирует multipart запрос
  • FastAPI/Starlette автоматически парсят файлы через UploadFile
  • Всегда проверяй размер, тип и источник файла