Многие ресурсы используют файловые хранилища. Кроме возможностей
загрузки и хранения файлов, бывает необходимо организовать их
скачивание. Одно дело, когда файлы лежат в открытом доступе, но и тогда
может потребоваться передача файла через PHP. Например, администратору
ресурса может быть нужна информация о количестве скачиваний. Для файлов
большого объема до сих пор требуется возможность докачки, что, пожалуй, и
является самым трудным моментом для серверных скриптов. Посмотрим, как
можно организовать работу скрипта на PHP, который позволяет реализовать
все вышеуказанные возможности.
Начнем с простого способа. Пусть наш скрипт получает имя файла через
какой-либо из параметров запроса. Это может быть реально набранный URL,
а может быть и переписанный сервером при помощи mod_rewrite. Скрипт
вызывает функцию file_download() с параметром $filename. Кроме прямой
передачи в запросе $filename может также вычисляться на основе
исходного идентификатора из запроса или дополняться путем в дереве
папок сервера.
Самый легкий способ обработки запросов к скачиваемым файлам - это простое перенаправление на них.
function file_download($filename){ // Проверяем существование файла if(file_exists($filename)){ // Здесь пишем код, который будет обрабатывать каждую загрузку файла.
// Перенаправляем клиента на файл. header('Location: '.$filename); }else{ // Если файл не найден, сообщаем клиенту об этом через заголовки HTTP header($_SERVER["SERVER_PROTOCOL"].' 404 Not Found'); header('Status: 404 Not Found'); } // Прерываем дальнейшее выполнение скрипта, чтобы не отправлять мусор в ответе клиенту exit; }
Данный способ позволяет учитывать число закачек, но после
перенаправления адрес файла становится доступным напрямую. Поэтому мы
лишены возможности программно контролировать как саму закачку файлов,
так и скрыть их реальные адреса или взять их из папки, недоступной по
протоколу HTTP.
Для этого потребуется усложнить нашу функцию закачки.
function file_download($filename,$mimetype='application/octet-stream'){ if(file_exists($filename)){ // Отправляем требуемые заголовки header($_SERVER["SERVER_PROTOCOL"].' 200 OK'); // Тип содержимого. Может быть взят из заголовков полученных от клиента // при закачке файла на сервер. Может быть получен при помощи расширения PHP Fileinfo. header('Content-Type: '.$mimetype); // Дата последней модификации файла header('Last-Modified: '.gmdate('r',filemtime($filename))); // Отправляем уникальный идентификатор документа, // значение которого меняется при его изменении. // В нижеприведенном коде вычисление этого заголовка производится так же, // как и в программном обеспечении сервера Apache header('ETag: '.sprintf('%x-%x-%x',fileinode($filename),filesize($filename),filemtime($filename))); // Размер файла header('Content-Length: '.(filesize($filename))); header('Connection: close'); // Имя файла, как он будет сохранен в браузере или в программе закачки. // Без этого заголовка будет использоваться базовое имя скрипта PHP. // Но этот заголовок не нужен, если вы используете mod_rewrite для // перенаправления запросов к серверу на PHP-скрипт header('Content-Disposition: attachment; filename="'.basename($filename).'";'); // Отдаем содержимое файла echofile_get_contents($filename); }else{ header($_SERVER["SERVER_PROTOCOL"].' 404 Not Found'); header('Status: 404 Not Found'); } exit; }