Возникла задача отслеживания загрузок файлов с сайта (изображений, документов, видео, дистрибутивов, …), т.к. обычные сервисы статистики не могут это делать без изменения URL файлов. И статистика должна быть видна в привычном месте (например, Google Analytics или FireBase).
Перебрав несколько плагинов (большинство имеют в названии слова Download и Manager), я обнаружил, что все они организованы по принципу ручного составления списка файлов для мониторинга. И во многих из них реализованы защиты от неавторизированного скачивания, что в данной задаче избыточно. Можно было бы воспользоваться ими, но если файлов много, то в итоге:
- слишком неудобно и долго заводить элемент под каждый файл;
- файлы могут менять свое расположение – опять придется исправлять элемент.
В итоге сделана своя реализация в виде плагина к WordPress, в котором просто указывается каталог (относительный путь сайта) и далее происходит мониторинг загрузок его содержимого.
Ссылка на бесплатный плагин тут для тех кому информации выше достаточно. Дальше приведены примеры результатов статистик и подробности технической реализации.
Куда отправляется статистика
Пока поддержаны два самых базовых места агрегации статистики.
Google Analytics
Статистика публикуется в виде сообщений (Events), у которых через настройки задается категория (Event Category), в действии (Event Action) указывается URI до файла и в метке сообщения (Event Label) указываются параметры запроса, если выставлена соответствующая настройка. В итоге можно удобно наблюдать динамику загрузок каждого файла в каталоге в консоли Google Analytics.
Таблица в базе данных WordPress
Служит в основном для отладки. Тут просто происходит подсчет количества скачиваний, временная динамика не видна. Поля таблицы: IP, URI файла, параметры запроса (если были) и счетчик. Данные можно увидеть любым редактором SQL (например, phpMyAdmin).
Каждой записи назначается ID для их удаления по отдельности при необходимости.
Перехват обращений к файлам
Загрузка файлов обрабатывается самим Web сервером Apache, поэтому сделан обработчик в .htaccess c перенаправлением в PHP скрипт.
Выглядит это так:
<FilesMatch "\.(.*)$">
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteCond %{REQUEST_URI} !\.(htaccess|php|js|css)$
RewriteCond %{REQUEST_URI} ^/mypath/(.*)
RewriteRule ^(.*) /index\.php\?seraph_dlstat_api=Get&uri=%{REQUEST_URI} [L,QSA]
</IfModule>
</FilesMatch>
Специально сделаны исключения для системных файлов с типами htaccess, php, js, css.
Для минимизации времени срабатывания, вызов скрипта реализован через параметр seraph_dlstat_api для index.php, который проверяется почти сразу после загрузки всех скриптов WordPress, необходимых для обработки. Сделано это на action-хуке do_parse_request – самом первом callback после загрузки всего рабочего окружения (выполнения wp-load.php).
Далее, скрипт обрабатывает\регистрирует URI и возвращает содержимое файла через системную функцию readfile. Также, поддержана частичная загрузка файлов через HTTP_RANGE, где файл уже читается блоками.
Отложенная отсылка данных
Для максимального уменьшения времени отклика поддержана асинхронная отправка статистики. При обращении к файлу создается запись в БД и файл сразу возвращается клиенту. А уже на срабатывании шедулера WordPress (WP Cron) данные берутся пачкой из таблицы и делается отсылка статистики.
Для Google Analytics это допустимо, т.к. она поддерживает асинхронный прием сообщений через указание времени задержки.
По умолчанию WP Cron срабатывает при любой загрузке страницы. Можно настроить WP Cron от системного шедулера для еще большей оптимизации времени отклика.
Заключение
В итоге для клиента загрузка файла ничем неотличима от стандартной обработки Web сервером и теперь есть возможность отслеживать это.
Буду признателен за любой фидбэк.
jarrus
Если есть доступ к настройке сервера то лучше использовать X-SendFile, т.е. не передавать файл средствами PHP а возвращать файл уже через Apache, аналогичная вещь — X-Accel-Redirect в NGinx, использовали ее для проверки прав доступа к приватным файлам.
Чуть больше информации на эту тему тут: https://habrahabr.ru/post/151795/
polnd Автор
Да, спасибо, полезно. Но для апдейта HTTP заголовка уже требуется запуск PHP — т.е. время уже на это потрачено. Дальше же отдача файла на клиент делается оптимизированной функцией readfile.