Отправка фото и документов в Telegram через PHP: multipart, file_id и лимиты Bot API

Отправка файлов — одна из базовых операций при интеграции Telegram-бота с бизнесом: чеки, каталоги, прайсы, фотографии товаров. Казалось бы, Bot API покрывает всё в двух методах — sendPhoto и sendDocument. Но есть нюансы: cURL не отправляет файлы как обычный POST-массив, file_id можно переиспользовать, а размер файлов ограничен жёстче, чем кажется. Разберём всё по порядку.

Почему не file_get_contents для API

В учебных примерах часто встречается отправка данных через file_get_contents с потоком http://. Это работает для простых запросов, но не для файлов. Telegram принимает фото и документы только через multipart/form-data — формат, в котором каждый параметр (включая файл) кодируется как отдельная часть тела запроса с заголовками Content-Disposition.

Стандартная библиотека cURL в PHP делает это нативно: нужно лишь указать CURLOPT_POST, передать текстовые поля через CURLOPT_POSTFIELDS как массив, а файл — ключом с префиксом @ (или объектом CURLFile в PHP 5.5+).

sendPhoto: отправка локального файла

Минимальный рабочий пример отправки фотографии, которая уже лежит на диске сервера:

Обратите внимание: CURLFile принимает MIME-тип и оригинальное имя файла. Telegram сам определит формат, но указание image/jpeg или image/png ускоряет валидацию.

sendDocument: отправка PDF, XLSX, ZIP

Принцип тот же, но с двумя отличиями: лимит больше (до 50 МБ против 10 МБ у фото) и можно передать caption — текст до 1024 символов. Для прайсов, инвойсов и договоров это стандартный сценарий:

file_id: повторное использование вместо повторной загрузки

Когда вы отправляете файл первый раз, Telegram возвращает объект Photo или Document с полем file_id. Этот идентификатор — строковое значение вида AgACAgIAAxkBAAIBZ2.... Его можно использовать в последующих запросах без повторной загрузки файла:

Это критически важно для каталогов: загружаете изображение товара один раз при создании карточки, сохраняете file_id в базу, а при отправке клиенту — просто передаёте идентификатор. Экономите и трафик, и время API-вызова.

Важно: file_id привязан к боту. Идентификатор, полученный в одном боте, нельзя использовать в другом. Срок жизни file_id не документирован, но на практике остаётся рабочим месяцами.

Лимиты размера: 10 МБ для фото, 50 МБ для документов

Telegram устанавливает следующие ограничения:

  • sendPhoto — локальный файл до 10 МБ, по URL до 10 МБ;
  • sendDocument — локальный файл до 50 МБ, по URL до 50 МБ (для некоторых типов файлов может быть меньше);
  • фото автоматически сжимается до 1280×1280px, если превышает.

На практике проверяйте размер filesize до отправки. Если файл больше лимита — отправляйте архивом или пережимайте.

Когда передавать URL вместо загрузки

У sendPhoto и sendDocument есть альтернативный способ: параметр photo или document принимает прямую ссылку на файл в интернете. В этом случае Telegram сам скачает контент — ваш сервер не тратит трафик на загрузку и отправку.

Используйте URL, когда:

  • файл уже лежит в CDN, S3 или публичном хранилище;
  • не хотите загружать один и тот же файл повторно при каждой отправке;
  • ваш сервер ограничен по исходящему трафику или времени выполнения.

Используйте multipart-загрузку, когда:

  • файл генерируется динамически (PDF-счёт, отчёт) и не хранится публично;
  • нужно гарантировать доступность файла (URL может «умереть»);
  • требуется caption с разметкой (эмодзи, форматирование) — работает и с URL, и с file_id.

Типичные ошибки

Ошибка: «Bad Request: wrong URL host». Telegram не принимает ссылки с авторизацией (https://user:pass@example.com/file.jpg) и локальные адреса (http://localhost/...). Сервер бота должен иметь доступ к URL.

Ошибка: пустой CURLFile или файл не найден. Проверяйте file_exists() перед отправкой. Если файл генерируется в памяти (например, через imagejpeg()), записывайте во временный файл через tempnam() или используйте поток php://memory с обёрткой CURLFile.

Ошибка: «File is too big». Проверяйте filesize и лимиты метода. Если отправляете по file_id, лимиты не применяются — повторная отправка того же файла не ограничена.

Итог: когда какой подход

Для каталогов и статичных медиа — загрузили один раз, сохранили file_id в базу, отправляем по идентификатору. Для динамических документов (счета, договоры) — генерируем, отправляем через CURLFile. Для контента из CDN — передаём URL, экономим ресурсы сервера.

Правильная работа с файлами в Telegram-боте сокращает время отклика, экономит трафик и снижает нагрузку на Bot API в рамках лимитов 20–60 сообщений в секунду. Для сложных интеграций с файлами рассмотрите хостинг ботов с быстрым диском и достаточным исходящим каналом.

Новые статьи — в Telegram

Разбираем, что автоматизировать в бизнесе и как это работает на практике. Без спама.