Как загружать файлы в Catalyst-приложении

Загрузка файлов в Catalyst-приложении с помощью стандартного средства Catalyst::Request::Upload. Работа с библиотекой lib::abs.

Задача: требуется организовать загрузку пользовательских файлов на сайт.

Реализация:

  1. Страница с полным списком товаров и вывод пригрепленного к товару изображения.
  2. Страница добавления нового товара.
  3. Страница редактирования товара и поле для загрузки изображений.

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

 

Изменения в базе данных

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

 

Модель — доступ к файловому хранилищу

Зачем нам тут модель? Файлы, после получения от клиента — надо куда-то сохранять. Можно сохранять файлы прямо в специально отведенные директории, в рамках файловой структуры сайта.

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

Еще хуже, если реализуется социальная сеть. Не так уж редко, один пользователь загружает больше тысячи фотографий себя любимого, и еще некоторое количество видео-файлов. Умножим это количество файлов на несколько тысяч пользователей (не говоря уж о том, что пользователей может быть несколько сотен тысяч).

Разместить такое количество данных в директории /myapp/root/images — занятие крайне не благодарное и опасное. Лучше использовать для хранения файлов специальные файловые хранилища и отдельные сервера, которые будут специализироваться на работе со статикой.

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

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

В моем примере модель реализуется самая простая, главное просто обозначить, что она есть. Проверки типов, имен файлов — не производятся. Если попытаться записать дважды файл, с одним и тем же именем, вероятнее всего, произойдет ошибка. Файлы сохраняются в отдельную директорию проекта. Как уже упоминалось выше, это не правильно «in real life», но для примера — вполне допустимо.

\lib\app\Model\FileStorage.pm :

 

Библиотека lib::abs, документация

Библиотека lib::abs помогает преобразовать относительные пути в абсолютные. Пример:

Результат выполнения:

Допустимый вариант использования:

Указанные инструкции будут обрабатываться в тот же момент, когда начнется стадия выполнения BEGIN-блока, и повлияет на то, какие данные будут помещены в @INC. Но такой вариант применения lib::abs встречается значительно реже чем первый, с использованием функции path().

 

Контроллер — загрузка файлов, их удаление и просмотр

Особенности реализации.

Так же, как и модель, контроллер сильно упрощен. Отсутствуют проверки на ошибки, нет возможности добавлять несколько изображений к одному товару. При создании нового элемента, добавление картинки не реализуется.

\lib\app\Controller\Admin\Catalog.pm :

В результате, данное приложение после запуска будет реагировать на следующие адресам:

  • http://localhost:3000/admin/catalog
  • http://localhost:3000/admin/catalog/create
  • http://localhost:3000/admin/catalog/1/edit
  • http://localhost:3000/admin/catalog/1/delete

 

Модуль Catalyst::Request::Upload, документация

Catalyst::Request::Upload — обрабатывает запросы на загрузку файлов в Catalyst. Чтобы задать место хранения временных файлов Catalyst, надо указать в конфиге:

По умолчанию, Catalyst будет использовать системную директорию для хранения временных файлов — temp .

$upload->new

Создание нового объекта Catalyst::Request::Upload. Объект создается автоматически, при вызове
my $upload = $c->req->upload(‘field’);

$upload->copy_to

Копирует временный файл в указанную директорию. Копирование реализовано с помощью File::Copy. Возвращает true в случае успешного завершения операции, и false — в случае неудачи.

$upload->fh

Возвращает файловый дескриптор (IO::File) для временного файла.

$upload->link_to

Создает жесткую ссылку для временного файла. Возвращает true в случае успешного завершения операции, и false — в случае неудачи.

$upload->headers

Возвращает объект HTTP::Headers для запроса, во время которого был загружен файл.

 

Общий пример для нескольких методов, приведенных ниже:

Вывод (запуск скрипта был под windows):

$upload->size

Возвращает размер загруженного файла в байтах.

$upload->basename

Возвращает имя файла. При этом, имя будет обработано регулярным выражением basename =~ s|[^\w\.-]+|_|g , чтобы избежать появления имен со специальными символами, которые могут некорректно обрабатываться операционной системой.

$upload->tempname

Возвращает путь к временному файлу, который был создан Catalyst-приложением после получения запроса от клиента.

$upload->type

Возвращает Content-Type для отправленного клиентом файла.

Для получения более полной информации, смотрите официальную документацию на модуль Catalyst::Request::Upload.

 

Шаблоны — формы для загрузки файлов

1. Страница со списком товаров
root\src\admin\catalog\list.tt :

2. Страница создания нового товара
root\src\admin\catalog\element_create.tt :

3. Страница редактирования товара
root\src\admin\catalog\element_edit.tt :

4. В каталоге root должен быть создан каталог static, в каталоге static создаем каталог filestorage, в каталоге filestorage создаем каталог images.

5. Для того, чтобы картинки отображались, в модуле \lib\app.pm правим конфигурационный блок:

 

Полезные ссылки

search.cpan.org: Catalyst::Request::Upload

search.cpan.org: Path::Class::File

search.cpan.org: lib::abs