Вопрос архитектурно-идеологического свойства имею
Заканчиваю делать экспорт KML и KMZ. Делаю в виде дополнительного энкодера, который базируется на ядерном xml-энкодере и просто добавляет к списку доступных для REST-экспорта форматов KML и KMZ. В принципе все получается, только вот что непонятно:
Согласно спецификации интерфейса, функция encode возвращает результат своей деятельности в виде текстовой переменной. Последующая упаковка этого результата в http response и его отдача - не забота энкодера, что и правильно. Для kml-формата, который по сути текст и есть, так же как xml и json, с этим никаких проблем нет. Но KMZ - по сути zip-файл, который помимо kml-файла может содержать ещё кучу всякой увесистой мельтимудии. И создаётся он, естественно, не внутри php-переменной, а сторонней утилитой во внешнем файле. Я, конечно, могу потом засосать этот файл в php-переменную, что сейчас и делаю, и всё работает, но берут меня сомнения - правильный ли это подход с т.з. производительности и потенциального падения сервера на слишком большом файле?
Теоретически я могу, наверное, прямо в энкодере сформировать BinaryFileResponse и принудительно послать его, по факту прервав исполнение всего последующего кода, но это тоже как-то неизящно будет - ведь теоретически энкодер может быть вызван из какого-нибудь нестандартного контекста, где отсылка респонса вообще не предусматривается?
Как бы поступили вы, уважаемые гуру?
Отдача большого зипа из контекста REST-энкодера
Главные вкладки
Лучший ответ
В общем, подумал и оставил как есть
Меня смущали две вещи: "бинарность" данных, помещаемых в строковую переменную, и их потенциальный размер.
Прочтение документации по PHP показало, что размещение произвольных бинарных данных (включая 0x00) в строке абсолютно штатная ситуация для PHP (подозреваю, что все, кроме меня, это знали давно, но я же не настоящий сварщик
С размером тоже все оказалось не так страшно: как выяснилось в результате экспериментов, единственная мельтимудия, которую гуглмапс согласен брать из kmz-файла, это иконки, а они маленькие, а с учётом того, что сам KML файл ужимается раз в десять, то размер kmz получается никак не больше исходного kml, который берется из той же переменной.
Мой код выложен на орг как 8.x-1.x-dev (https://www.drupal.org/project/kml/issues/2994088), кому надо - берите, пробуйте, комментируйте.
Всем спасибо за полезную дискуссию!
Комментарии
имхо конечно, но rest должен отдавать именно "текст" в нужном формате (xml,json и т.п.)
а уже в этом тексте должны быть поля с "атрибутами" файла (ссылка, наименование и т.п.)
В данном случае от REST'а фактически используется только инфраструктура, на самом деле не представляю себе выдачу KML/KMZ именно в REST-контексте - речь идёт об отдаче пользователю файла, который он сохраняет.
Я плясал от модуля Views Data Export, он предназначен для экспорта данных представления в разных форматах (именно то, что мне нужно для выгрузки наборов геомаркеров), и он базируется именно на "REST Export" дисплее (а не Feed, как модуль KML для семёрки), видимо, потому, что для REST уже сделан мощный и легко расширяемый набор энкодеров. Кстати, у него ещё имеется плагин для экспорта в формате XLS, который тоже бинарный, и он тоже выдаётся через "текстовую" переменную.
Бинарный формат отдает \Drupal\image\Controller\ImageStyleDownloadController::deliver
А там просто отдается контент файла и нужные заголовки.
У Вас один формат - xml
а xml views без всяких "помошников" умеет..
По аналогии с дисплеем Feed и стилем RSS
А вот как KML в зип завернуть перерасход ресурсов не получить, тут подумать надо..
если KML-файлы не слишком "динамичные", т.е. вьюс, который их формирует не имеет большое кол-во экспозед и контекстных фильтров, можно подумать как кэшировать эти файлы на диске.
Например по аналогии с тем же image-style
При первом запросе оригинальное изображение "стилизуется" и сохраняется на диск, а при последующих запросах стилизованное изображение отдается уже вэб-сервером(nginx и т.п.) сразу с диска.
Не знаю, какая у вас "логика" работы пользователей с KML-KMZ файлами, поэтому пофантазирую-)
я бы сделал в папке files 2 папки: kml и kmz
при первом запросе kml-файла создавал бы папку files/kml/[идентификатор запроса]
кидал бы в нее сам файл kml и симлинки на ресурсы
и отдавал по аналогии с \Drupal\image\Controller\ImageStyleDownloadController::deliver
kml-файл пользователю
при первом запросе kmz файла зиповал бы папку
files/kml/[идентификатор запроса] в файл
files/kmz/[идентификатор запроса].kmz
и отдавал kmz файл пользователю
пути ссылок на файлы должны соответствовать их физическому пути на диске
останется только настроить web-сервер, чтобы при наличии файла на диске он отдавал их напрямую.
как-то так-)
Да его много кто отдает, только не из нужного мне контекста Тому контроллеру request аж в параметрах передается на растерзание. А энкодеру с реквестом хозяйничать по рангу не положено
Ну это и так, и не так. Для формата HAL специальный модуль написан, хотя тоже можно сказать, что нет такого формата, а есть JSON. KML содержит совершено определенные теги, которые мой энкодер и помогает правильно заполнить, хотя парентом у него конечно XML-энкодер.
Я долго думал - на Feed мне ориентироваться или на REST Export/serialization. Семерочный модуль KML сделан на Feed, причем там генерация KML сделана совсем "на коленке", через шаблоны, даже без использования стандартных xml-инструментов. Но я семерку совсем не знаю (и, если честно, не очень хочу знать и мне не хотелось в нее вникать, чтобы перетащить семерочный модуль на восьмерку. А Views Data Export, базирующийся на ядерных rest и serialization, делает практически то, что мне нужно, причем уже с использованием современных восьмерочных инструментов, вот я на него и сориентировался. Я, кстати, не уверен, что в контексте дисплея Feed было бы проще отдать бинарный файл, там тоже
$response->setContent($output);
Строго-то говоря, ни Feed ни REST не заточены на генерацию скачиваемых пользователем файлов, и то и другое предназначено скорее для обмена информацией между клиентом и сервером без участия юзера и без скачивания файла на диск. А дальше же уж кто-то берет имеющийся инструмент, наиболее близкий к решаемой задаче, и подтачивает его под свою задачу.
В моем случае не вариант - файлы генерируются для каждого города (коих сотни), а в перспективе будет возможность экспортировать всё что попало в произвольное окошко карты по boundary фильтру.
Тут наверное нужно уточнить: меня-то вполне устраивает то, что получилось, просто хотел поделиться модулем с сообществом (когда добью до конца), и решил заранее провентилировать не слишком ли срамное решение выбрал
По своему опыту скажу, что ни Feeds, ни Migrations в восьмёрке лучше не использовать как основу для какого-то специфического или ключевого функционала, ибо основные фишки этих модулей - глючность, непроизводительность и могут отваливаться после каждого обновления ядра.
По поводу задачи - KML-формат тянет на то, чтобы быть REST-эндпоинтом. KMZ больше похож на контроллер. Архитектурно это совершенно разные вещи. Но интуитивно понятно, что по большей части функционал схож. Поэтому логичнее всего воткнуть этот функционал в сервис, чтобы обращаться к нему из рест-плагина и из контроллера. Можно ещё попробовать использовать трейт, но мне кажется, что в данной ситуации он не подойдёт.
Мне почему-то интуитивно так и казалось - спасибо за подтверждение
В том-то и дело, что и функционал схож, и код схож, и с точки зрения юзера это просто две ссылки, стоящие рядом - скачать как kml и скачать как kmz.
Подумаю эту мысль, спасибо.
В общем, подумал и оставил как есть
Меня смущали две вещи: "бинарность" данных, помещаемых в строковую переменную, и их потенциальный размер.
Прочтение документации по PHP показало, что размещение произвольных бинарных данных (включая 0x00) в строке абсолютно штатная ситуация для PHP (подозреваю, что все, кроме меня, это знали давно, но я же не настоящий сварщик
С размером тоже все оказалось не так страшно: как выяснилось в результате экспериментов, единственная мельтимудия, которую гуглмапс согласен брать из kmz-файла, это иконки, а они маленькие, а с учётом того, что сам KML файл ужимается раз в десять, то размер kmz получается никак не больше исходного kml, который берется из той же переменной.
Мой код выложен на орг как 8.x-1.x-dev (https://www.drupal.org/project/kml/issues/2994088), кому надо - берите, пробуйте, комментируйте.
Всем спасибо за полезную дискуссию!