- Отдаем файлы эффективно с помощью PHP
- 2. Читаем и отправляем файл вручную
- 3. Используем модуль веб сервера
- Отдаем файлы эффективно с помощью PHP
- Функция readfile()
- Чтение и отправка файла вручную
- Отдаем файл через сервер
- Отдаем файлы эффективно с помощью PHP
- 1. Используем readfile()
- 2. Читаем и отправляем файл вручную
- 3. Используем модуль Apache XSendFile
Отдаем файлы эффективно с помощью PHP
Метод хорош тем, что работает с коробки. Надо только написать свою функцию отправки файла (немного измененный пример из официальной документации):
function file_force_download($file) < if (file_exists($file)) < // сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт // если этого не сделать файл будет читаться в память полностью! if (ob_get_level()) < ob_end_clean(); >// заставляем браузер показать окно сохранения файла header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); // читаем файл и отправляем его пользователю readfile($file); exit; > >
Таким способом можно отправлять даже большие файлы, так как PHP будет читать файл и сразу отдавать его пользователю по частям. В документации четко сказано, что readfile() не должен создавать проблемы с памятью.
- Скрипт ждет пока весь файл будет прочитан и отдан пользователю.
- Файл читается в внутренний буфер функции readfile(), размер которого составляет 8кБ (спасибо 2fast4rabbit)
2. Читаем и отправляем файл вручную
Метод использует тот же Drupal при отправке файлов из приватной файловой системы (файлы недоступны напрямую по ссылкам):
function file_force_download($file) < if (file_exists($file)) < // сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт // если этого не сделать файл будет читаться в память полностью! if (ob_get_level()) < ob_end_clean(); >// заставляем браузер показать окно сохранения файла header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); // читаем файл и отправляем его пользователю if ($fd = fopen($file, 'rb')) < while (!feof($fd)) < print fread($fd, 1024); >fclose($fd); > exit; > >
- Скрипт ждет пока весь файл будет прочитан и отдан пользователю.
- Позволяет сэкономить память сервера
3. Используем модуль веб сервера
3a. Apache
Модуль XSendFile позволяет с помощью специального заголовка передать отправку файла самому Apache. Существуют версии по Unix и Windows, под версии 2.0.*, 2.2.* и 2.4.*
В настройках хоста нужно включить перехват заголовка с помощью директивы:
Также можно указать белый список директорий, файлы в которых могут быть обработаны. Важно: если у Вас сервер на базе Windows путь должен включать букву диска в верхнем регистре.
Описание возможных опций на сайте разработчика: https://tn123.org/mod_xsendfile/
function file_force_download($file) < if (file_exists($file)) < header('X-SendFile: ' . realpath($file)); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); exit; >>
3b. Nginx
Nginx умеет отправлять файлы из коробки через специальный заголовок.
Для корректной работы нужно запретить доступ к папку напрямую через конфигурационный файл:
Пример отправки файла (файл должен находиться в директории /some/path/protected):
function file_force_download($file) < if (file_exists($file)) < header('X-Accel-Redirect: ' . $file); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); exit; >>
Больше информации на странице официальной документации
- Скрипт завершается сразу после выполнения всех инструкций
- Физически файл отправляется модулем самого веб сервера, а не PHP
- Минимальное потребление памяти и ресурсов сервера
- Максимальное быстродействие
Update: Хабраюзер ilyaplot дает дельный совет, что лучше слать не application/octet-stream , а реальный mime type файла. Например, это позволит браузеру подставить нужные программы в диалог сохранение файла.
Отдаем файлы эффективно с помощью PHP
Когда вообще нужно отдавать файлы через PHP? Например, когда нужно собрать какую-то статистику скачиваний файла или осуществить запрет на скачивание файлов, позволив скачивать по ссылке только один раз и ограниченной группе пользователей.
Допустим, чтобы скачать какой-то файл, мы отправляем пользователя не на прямую ссылку с расположением файла:
а на адрес скрипта, который отдает пользователю файл через PHP:
/files/download.php?file=$file_id
Где, в параметр file, для скрипта можно передавать идентификатор файла, который требуется скачать, после чего можно выстраивать различные проверки и выдавать пользователю файл для скачивания:
Header('location: /uploads/files/' . $file);
Сделать это можно несколькими способами, о которых речь пойдет ниже.
Функция readfile()
Этот метод будем применять в специально созданной функции. Такая функция поможет отправлять даже большие файлы, PHP будет отдавать файл пользователю по частям. Функция ждет, когда файл будет прочтен и отдан.
/** * Отдача файла * Функция для отдачи файла через PHP * @param string $file путь к файлу на сервере * @return mixed */ function file_download($file) < if (file_exists($file)) < if (ob_get_level()) < ob_end_clean(); >header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); readfile($file); return [ 'status' => 'success', 'message' => 'Файл успешно отдан' ]; >else < return [ 'status' =>'error', 'message' => 'Файл не найден' ]; > >
Чтение и отправка файла вручную
Эта функция аналогична той, которая описана выше, но для чтения и отдали файла используются: fopen, feof, fread, fclose. Функция ждет когда файл будет прочитан и отдан, также позволяет экономить память.
/** * Отдача файла * Функция для отдачи файла через PHP * @param string $file путь к файлу на сервере * @return mixed */ function file_download($file) < if (file_exists($file)) < if (ob_get_level()) < ob_end_clean(); >header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); if ($fd = fopen($file, 'rb')) < while (!feof($fd)) < print fread($fd, 1024); >fclose($fd); > return [ 'status' => 'success', 'message' => 'Файл успешно отдан' ]; >else < return [ 'status' =>'error', 'message' => 'Файл не найден' ]; > >
Отдаем файл через сервер
Отдать файл можем не скриптом, через PHP, а с помощью Apache или Nginx. Отдача файла средствами сервера дает максимальное быстродействие, минимум потребляет памяти и ресурсов сервера.
Для Apache есть модуль XSendFile, который поможет с помощью специального заголовка сделать отправку файла Apache. В настройках хоста включитте директиву перехвата заголовка:
Функция для отправки файла будет следующей:
/** * Отдача файла * Функция для отдачи файла через PHP * @param string $file путь к файлу на сервере * @return mixed */ function file_download($file) < if (file_exists($file)) < header('X-SendFile: ' . realpath($file)); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); return [ 'status' =>'success', 'message' => 'Файл успешно отдан' ]; >else < return [ 'status' =>'error', 'message' => 'Файл не найден' ]; > >
Nginx умеет отправку файла из коробки, все что нужно, настроить конфиг, указав запрет на доступ к каталогу (my/path/protected/):
Функция отправки файла выглядит так:
/** * Отдача файла * Функция для отдачи файла через PHP * @param string $file путь к файлу на сервере * @return mixed */ function file_download($file) < if (file_exists($file)) < header('X-Accel-Redirect: ' . $file); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); return [ 'status' =>'success', 'message' => 'Файл успешно отдан' ]; >else < return [ 'status' =>'error', 'message' => 'Файл не найден' ]; > >
Отдаем файлы эффективно с помощью PHP
2012-09-19 в 14:53, admin , рубрики: Apache, php, Веб-разработка, настройка системы, метки: apache, PHP, настройка системы
Если Вам потребовалось отдавать файлы не напрямую веб сервером, а с помощью PHP (например для сбора статистики скачиваний), прошу под кат.
1. Используем readfile()
Метод хорош тем, что работает с коробки. Надо только написать свою функцию отправки файла (немного измененный пример из официальной документации):
function file_force_download($file) < if (file_exists($file)) < // сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт // если этого не сделать файл будет читаться в память полностью! if (ob_get_level()) < ob_end_clean(); >// заставляем браузер показать окно сохранения файла header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); // читаем файл и отправляем его пользователю readfile($file); exit; > >
Таким способом можно отправлять даже большие файлы, так как PHP будет читать файл и сразу отдавать его пользователю по частям. В документации четко сказано, что readfile() не должен создавать проблемы с памятью.
- Скрипт ждет пока весь файл будет прочитан и отдан пользователю.
- Файл читается в внутренний буфер функции readfile(), размер которого нигде не указан (может кто подскажет)
2. Читаем и отправляем файл вручную
Метод использует Drupal при отправке файлов из приватной файловой системы (файлы недоступны напрямую по ссылкам):
function file_force_download($file) < if (file_exists($file)) < // сбрасываем буфер вывода PHP, чтобы избежать переполнения памяти выделенной под скрипт // если этого не сделать файл будет читаться в память полностью! if (ob_get_level()) < ob_end_clean(); >// заставляем браузер показать окно сохранения файла header('Content-Description: File Transfer'); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); header('Content-Transfer-Encoding: binary'); header('Expires: 0'); header('Cache-Control: must-revalidate'); header('Pragma: public'); header('Content-Length: ' . filesize($file)); // читаем файл и отправляем его пользователю if ($fd = fopen($file, 'rb')) < while (!feof($fd)) < print fread($fd, 1024); >fclose($fd); > exit; > >
- Скрипт ждет пока весь файл будет прочитан и отдан пользователю.
- Позволяет сэкономить память сервера
3. Используем модуль Apache XSendFile
Модуль XSendFile позволяет с помощью специального заголовка передать отправку файла самому Apache. Существуют версии по Unix и Windows, под версии 2.0.*, 2.2.* и 2.4.*
В настройках хоста нужно включить перехват заголовка с помощью директивы:
Также можно указать белый список директорий, файлы в которых могут быть обработаны. Важно: если у Вас сервер на базе Windows путь должен включать букву диска в верхнем регистре.
Описание возможных опций на сайте разработчика: https://tn123.org/mod_xsendfile/
function file_force_download($file) < if (file_exists($file)) < header('X-SendFile: ' . realpath($file)); header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename=' . basename($file)); exit; >>
- Скрипт завершается сразу после выполнения всех инструкций
- Физически файл отправляется модулем самого Apache, а не PHP
- Минимальное потребление памяти и ресурсов сервера
- Максимальное быстродействие