Отправка файлов — одна из базовых операций при интеграции 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 сообщений в секунду. Для сложных интеграций с файлами рассмотрите хостинг ботов с быстрым диском и достаточным исходящим каналом.