Как отправлять файлы и изображения через API: пример с Python requests

📤 Кратко: Научитесь отправлять файлы через API на Python: изображения, PDF, логи — с обработкой ошибок и проверкой ответа сервера.
Как с помощью python api загрузить файл

Когда то давно мне нужно было автоматически загружать отчёты в систему партнёра: каждый день — новый CSV-файл с данными. Я думал, что мне придётся разбираться с multipart-формами и заголовками вручную, а оказалось — библиотека requests делает это за вас сама автоматически.

В этой статье я покажу пошагово, как:

  • Отправлять изображения, PDF, логи и любые файлы через API
  • Использовать requests.post(files=...) правильно
  • Обрабатывать ошибки и проверять ответ сервера
  • Работать с реальными сервисами (например, Telegram Bot API или тестовыми эндпоинтами)

Всё — с рабочим кодом и пояснениями, чтобы вы могли сразу вставить в свой скрипт.

Как работает отправка файлов через HTTP

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

API-серверы ожидают именно такой формат. К счастью, requests автоматически формирует его, если вы передаёте параметр files.

📚 Документация: Requests — File Uploads

Шаг 1. Базовая отправка файла

Подготовка и простой пример

import requests

# Путь к файлу
file_path = "report.pdf"

# Открываем файл в бинарном режиме
with open(file_path, "rb") as f:
    files = {"file": f}  # "file" — имя поля, как в <input name="file">
    response = requests.post("https://httpbin.org/post", files=files)

print("Статус:", response.status_code)
print("Ответ сервера:", response.json()["files"].keys())

httpbin.org/post — отличный демо-сервис для тестирования загрузок.

Шаг 2. Отправка с именем и MIME-типом

Иногда сервер проверяет расширение или тип файла. Укажите их явно:

with open("photo.jpg", "rb") as f:
    files = {
        "document": (
            "my_photo.jpg",  # желаемое имя файла
            f,
            "image/jpeg"     # MIME-тип
        )
    }
    response = requests.post("https://httpbin.org/post", files=files)

💡 Распространённые MIME-типы:

  • image/jpeg, image/png
  • application/pdf
  • text/plain, text/csv
  • application/octet-stream (универсальный)

Шаг 3. Отправка нескольких файлов

files = [
    ("images", ("1.jpg", open("1.jpg", "rb"), "image/jpeg")),
    ("images", ("2.png", open("2.png", "rb"), "image/png")),
    ("report", ("data.csv", open("data.csv", "rb"), "text/csv"))
]

response = requests.post("https://httpbin.org/post", files=files)

# Не забудьте закрыть файлы!
for _, (_, f, _) in 
    f.close()

Или используйте контекстные менеджеры для безопасности.

🔍 Что такое «контекстный менеджер»?

В Python контекстный менеджер — это конструкция with ... as ..., которая автоматически открывает и закрывает ресурс (например, файл), даже если в коде произошла ошибка.

Без него вы должны вручную писать file.close() — и легко забыть это сделать.
С ним — Python сделает всё за вас.

❌ Пример без контекстного менеджера (опасно!)

# Опасный способ
files = []
f1 = open("1.jpg", "rb")
f2 = open("2.png", "rb")
f3 = open("report.csv", "rb")

files = [
    ("images", ("1.jpg", f1, "image/jpeg")),
    ("images", ("2.png", f2, "image/png")),
    ("report", ("report.csv", f3, "text/csv"))
]

try:
    response = requests.post("https://httpbin.org/post", files=files)
    print("Успех!")
except Exception as e:
    print("Ошибка:", e)
# А вот здесь файлы НЕ закрыты! Они останутся открытыми в памяти.

⚠️ Если произойдёт ошибка, файлы не закроются, и это может привести к:

  • Утечке памяти
  • Блокировке файла (на Windows нельзя удалить открытый файл)
  • Достигнутому лимиту открытых файлов в системе

✅ Правильный способ: используем with для каждого файла

# Безопасный способ — один файл
with open("photo.jpg", "rb") as f:
    files = {"file": f}
    response = requests.post("https://httpbin.org/post", files=files)
# Файл автоматически закрыт здесь — даже если был исключение!

Но как быть с несколькими файлами? Ведь with можно вложить:

# Безопасный способ — несколько файлов
with open("1.jpg", "rb") as f1, \
     open("2.png", "rb") as f2, \
     open("report.csv", "rb") as f3:

    files = [
        ("images", ("1.jpg", f1, "image/jpeg")),
        ("images", "2.png", f2, "image/png")),
        ("report", ("report.csv", f3, "text/csv"))
    ]
    response = requests.post("https://httpbin.org/post", files=files)

# Все файлы закрыты автоматически!

💡 Обратите внимание на **обратный слеш \** — он позволяет переносить длинную строку with.

💡 Альтернатива: временные файлы в списке (для динамического количества)

Если файлов много и их имена хранятся в списке:

file_paths = ["1.jpg", "2.png", "3.gif"]
files_data = []

# Открываем все файлы безопасно
with ExitStack() as stack:
    for path in file_paths:
        # ExitStack автоматически закроет все файлы
        f = stack.enter_context(open(path, "rb"))
        mime = "image/jpeg" if path.endswith(".jpg") else "image/png"
        files_data.append(("files", (os.path.basename(path), f, mime)))
    
    response = requests.post("https://httpbin.org/post", files=files_data)

Для этого нужно:

from contextlib import ExitStack
import os

📚 ExitStack — встроенный инструмент для работы с динамическим числом ресурсов.

✅ Вывод для начинающего пользователя

Всегда используйте with open(...) as f: — это:

  • Гарантирует, что файл закроется
  • Делает код короче и надёжнее
  • Стандарт де-факто в Python

Простое правило:

Если вы открываете файл — делайте это внутри with. Всегда.
Тогда вы никогда не забудете закрыть файл, даже если скрипт упадёт.

Шаг 4. Реальный пример: отправка фото в Telegram

Подготовка

Вам понадобится:

Код отправки

def send_photo_to_telegram(photo_path, caption=""):
    token = "ваш_токен"
    chat_id = "ваш_id"
    url = f"https://api.telegram.org/bot{token}/sendPhoto"
    
    with open(photo_path, "rb") as photo:
        files = {"photo": photo}
        data = {"chat_id": chat_id, "caption": caption}
        response = requests.post(url, files=files, data=data)
    
    if response.status_code == 200:
        print("&#x2705; Фото отправлено")
    else:
        print("&#x274c; Ошибка:", response.json())

💡 Это настоящий кейс из автоматизации отчётов.

Шаг 5. Обработка ошибок и проверка ответа

Всегда проверяйте статус и содержимое ответа:

try:
    response = requests.post(
        "https://api.example.com/upload",
        files={"file": open("data.csv", "rb")},
        timeout=30
    )
    response.raise_for_status()  # вызовет исключение при 4xx/5xx
    
    result = response.json()
    if result.get("success"):
        print("Файл принят, ID:", result["file_id"])
    else:
        print("Сервер отклонил файл:", result.get("error"))
        
except requests.exceptions.RequestException as e:
    print(f"Ошибка сети: {e}")
except ValueError:
    print("Ответ не в формате JSON")
finally:
    # закрытие файла, если не использовали with
    pass

Советы для продвинутого использования

  • Используйте with open() — файлы закроются автоматически
  • Добавьте timeout — чтобы скрипт не «завис»
  • Для больших файлов — рассмотрите стриминг (stream=True)
print(f"Отправляю {os.path.getsize(path)} байт")

Заключение

Надеюсь статья была полезной и вы теперь вы умеете:

✅ Отправлять любые файлы через API
✅ Указывать имя и MIME-тип
✅ Работать с Telegram, httpbin и другими сервисами
✅ Обрабатывать ошибки и проверять ответы

Этот подход используется в:

  • Автоматической отправке отчётов
  • Интеграции с CRM и хранилищами
  • Тестировании API на бэкенде

🐍 requests делает загрузку файлов простой — без ручного формирования multipart.

🔁 Если вы работаете с API, посмотрите также:
python api запрос — получение данных и обработка JSON
python telegram api send message — отправка уведомлений и медиа
python github api — примеры с авторизацией и пагинацией
💬 Остались вопросы? Пишите в комментариях — с радостью уточню, дополню или помогу с вашим кодом.
📢 Подписывайтесь на Telegram-канал PythonAuto, чтобы не пропустить новые гайды по автоматизации, парсингу и Python.
👉 Ваш интерес — лучшая мотивация для новых статей!

Оставьте комментарий