Первая часть. Общие вопросы — https://habr.com/ru/articles/1047336/

Вторая часть. Установка библиотеки и пример работы с ней — https://habr.com/ru/articles/1048882/

В сегодняшней публикации планировалось просто сделать вторую часть учебной задачи, посвящённую загрузке файлов. Но так сложилось, что в библиотеке методы загрузки были только написаны и ввиду отсутствия "боевого применения" на моих проектах не проверялись должным образом на практике. А проверки по ходу работы над учебным примером кода показали, что библиотека в части загрузки файлов требует существенной доработки, если её делать строго так, как описано в официальной документации. Соответственно библиотека была доведена до ума, учебный пример был много раз прогнан на разных типах файлов, и только сейчас я могу говорить, что код библиотеки в части функционала загрузки файлов живой и работающий. А попутно захотелось поделиться с начинающими разработчиками своих библиотек поднабитыми шишками. Это и сделаю сначала, а дальше разберём собственно загрузку файлов через библиотеку.

Скрытый текст

Лирически-теоретическое отступление про загрузку файлов

Загрузка файлов осуществляется в два этапа — получение ссылки на загрузку и собственно загрузка файла, в результате (или до) которой выдаётся строковой токен файла, передаваемый в объект вложений к сообщению. Но нормально документировано из них только получение ссылки. А по собственно загрузке файлов запросы идут на разные сервисы в зависимости от типа. И ответы на них, что называется, «кто в лес, кто по дрова». По-видимому, связано это с тем, что в зависимости от типа загружаемого файла загрузка осуществляется в файловые хранилища разных проектов группы VK, которые разрабатываются и поддерживаются разными командами — каждая как знает и как умеет. Подготовил табличку с информацией о том, что возвращается в ответ на запрос собственно загрузки файла.

Тип загрузки

Тип ответа

Содержимое ответа

Примечание

image

JSON

Объект photos с вложенным подобъектом случайного имени, у которого единственное свойство token с токеном

video и audio

XML-подобноэ

<retval>1</retval> при успехе; ошибки не проверял

Токен выдаётся сразу в ответе первого запроса (параметр token)

file

JSON

Объект со свойствами fileId (цифровое значение; видимо, идентификатор файла) и token (нужный нам токен)

В библиотеке, конечно, эти все ответы обрабатываются автоматически, и токен забирается из одного параметра вне зависимости от типа загружаемого файла. Полагаю, рекомендация коллегам-разработчикам библиотек поступать аналогично не будет ошибкой.

Вернёмся к учебной задаче

В прошлой части мы договорились, что приложение, с которым работает библиотека, у нас по умолчанию установлено в каталоге /var/maxbot. Здесь и в последующих частях это соглашение также будет соблюдаться; все примеры будут из этого исходить.

Прежде чем продолжать, библиотеку нужно обновить. С момента выхода прошлой части там был исправлен ряд багов.

Также, согласно документации, API-запрос GET /chats начиная с июня 2026 года поддерживаться перестал. Поэтому из библиотеки были удалены объекты запроса lubezniy\yii2max\request\GetChats

и ответа на него

lubezniy\yii2max\response\GetChatsResponse

Кроме того, для сегодняшнего примера в каталог examples/tutorial/files добавлены файлы всех видов, один из которых мы используем в сегодняшнем примере для загрузки с типом file; на других файлах вы дополнительно можете отработать загрузку самостоятельно.

Для обновления либы переходим в её каталог и гитом вытягиваем свежую ветку main:

cd /var/maxbot/vendor/lubezniy/Yii2-max
git pull

Как загружать файлы через библиотеку

Для вызова соответствующих функций библиотеки нам нужны три вещи:

  • тип загружаемого файла согласно документации Bot API;

  • MIME-тип файла;

  • полное имя файла в файловой системе.

Конечно, тип из первого пункта списка частично можно определить по MIME-типу. Но это если бы не тип file . Хотим отправить фото или видео как файл - надо тоже указывать file , а не image или video . Возможно, в будущем для тех, кто не хочет заморачиваться, сделаю общий метод с автоприсвоением типа. А пока как есть.

Последовательность процедуры загрузки такова:

  1. Методом модуля createRequest создаём запрос класса lubezniy\yii2max\request\UploadFilePrepare . В его параметр type вписываем тип загружаемого файла из документации (первый пункт перечня необходимого).

  2. Как обычно, отправляем запрос методом send без параметров. Метод, если не будет ошибок, вернёт в ответ объект класса lubezniy\yii2max\response\UploadFilePrepareResponse . Созданный объект нужно обязательно запомнить в какой-нибудь переменной. Это не совсем обычный тип ответа; у него есть дополнительный метод processUpload, реализующий собственно отправку запроса на загрузку файла. И этот метод нужно вызвать для реализации второго этапа загрузки. В параметры метода передаются сначала полное имя загружаемого файла, затем его MIME-тип. Третьим необязательным параметром можно указать имя файла, под которым файл будет загружен в файлохранилище MAX. Результат выполнения этого метода нас интересует мало, его можно даже не запоминать - просто ловим исключения при ошибке.

  3. После выполнения обоих этапов загрузки осталось только получить токен загруженного файла для подстановки в сообщение. В зависимости от типа файла он появляется или после первого, или после второго этапа, но всегда находится в свойстве token объекта класса UploadFilePrepareResponse. Рекомендация — забирать его значение после выполнения второго этапа; так оно точно будет присутствовать на месте.

Примечание: Документация по API говорит нам о том, что видеофайлы после загрузки проходят ещё процесс подготовки. И, если попытаться отправить сообщение с вложением до окончания этого процесса (на больших видео он может занимать довольно значительное в масштабах исполнения веб-приложения время), то отправка сообщения вернёт ошибку. Сам пока не сталкивался с загрузкой больших видео и не могу сказать чего-либо конкретного на эту тему. Но эти моменты явно надо иметь в виду при реализации своих проектов и не торопиться отправлять сообщение с вложением сразу после загрузки большого видеофайла. Хотя бы несколько секунд стоит подождать.

Наконец, практическая реализация в учебной задаче

Надеюсь, вы ещё не потёрли с прошлой публикации файл /var/maxbot/commands/MaxLearnController.php . Идём в каталог приложения и запускаем этот файл на редактирование:

cd /var/maxbot
nano commands/MaxLearnController.php

В конец файла, в строку между двумя последними закрывающими фигурными скобками (если её нет, то нужно добавить пустую), вписываем action загрузки файла (в примере загрузим docx и выведем на экран полученный токен):

    /**
     * Шаг 2. Пример загрузки файла
     * @return int
     */
    public function actionStep2(): int
    {
        // Загрузка файла
        /**
         * @var \lubezniy\yii2max\Module $module Модуль бота
         */
        /**
         * @var \lubezniy\yii2max\request\UploadFilePrepare $request
         *  Объект запроса на первый этап загрузки
         */
        /**
         * @var \lubezniy\yii2max\response\UploadFilePrepareResponse $prepareResult
         * Объект ответа на первый этап и на всю загрузку
         */
        $module = Yii::$app->getModule('maxbot', true);

        // Создаём объект запроса на первый этап
        $request = $module->createRequest([
            'class' => \lubezniy\yii2max\request\UploadFilePrepare::class,
            'type' => 'file',
        ]);
        // Отправляем запрос, запоминаем результат
        $prepareResult = $request->send();
        // и пробуем загрузить файл        
        $prepareResult->processUpload(
            __DIR__ . '/../vendor/lubezniy/Yii2-max/examples/tutorial/files/docxfile.docx',
            'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
            'docxfile.docx'
        );
        // Выводим токен загруженного файла
        $this->stdout("Uploaded file token is " . $prepareResult->token . "\n");

        return ExitCode::OK;
    }

Если хотите поэкспериментировать с другими типами файлов, можно поменять реквизиты при необходимости. Ещё несколько файлов на пробу лежит в репозитории библиотеки; можно прописывать даже относительный путь к ним, как описано в коде. А пока сохраняем файл, выходим из редактора и проверяем, что у нас получилось:

php yii max-learn/step2

Если всё правильно, то в результате мы увидим на экране сообщение с токеном загруженного файла. Токен просьба записать на будущее; он пригодится в следующий раз, когда мы к кнопкам отправленного сообщения добавим загруженный файл.

Подытожим

Сегодняшний материал получился узкоспециализированным и относительно небольшим, но работа по нему, надеюсь, окажется кому-то полезной. В следующем материале будем разбирать редактирование сообщений и опробуем создание сообщений с разными категориями и количеством вложений. Может быть, и библиотеку снова придётся доработать, если в этот раз чего-то не учёл. Освоение и тестирование продолжаются.