У меня возникла следующая проблема с моим VPS-сервером.
У меня длинный PHP-скрипт, который отправляет большие файлы в браузер. Он делает что-то вроде этого:
<?php header("Content-type: application/octet-stream"); readfile("really-big-file.zip"); exit(); ?>
Это в основном считывает файл из файловой системы сервера и отправляет его в браузер. Я не могу просто использовать прямые ссылки (и пусть Apache обслуживает файл), потому что в приложении есть прикладная логика.
Проблема в том, что пока такая загрузка запущена, сайт не отвечает на другие запросы.
Проблема, с которой вы столкнулись, связана с тем, что вы используете сеансы. Когда у сценария есть работающий сеанс, он блокирует файл сеанса, чтобы предотвратить одновременную запись, которая может повредить данные сеанса. Это означает, что несколько запросов от одного и того же клиента – с использованием одного и того же идентификатора сеанса – не будут выполняться одновременно, они будут поставлены в очередь и могут выполнять только одно за раз.
У многих пользователей эта проблема не возникнет, так как они будут использовать разные идентификаторы сеанса. Это не означает, что у вас нет проблемы, потому что вы, возможно, захотите получить доступ к сайту во время загрузки файла или установить сразу несколько загрузок файлов.
Решение на самом деле очень просто: вызовите session_write_close()
прежде чем вы начнете выводить файл. Это закроет файл сеанса, освободит блокировку и разрешит выполнение последующих параллельных запросов.
Возможно, настройка вашего сервера – это не единственное место, которое вы должны проверить.
Попробуйте сделать запрос из своего браузера, как обычно, а затем сделайте еще один от другого клиента.
Либо wget с той же машины или другого браузера на другой машине.
Каким образом сервер не отвечает на другие запросы? Это «Ожидание example.com …» или это дает ошибку любого рода?
Я делаю что-то подобное, но я обслуживаю файл chunked, который дает файловой системе разрыв, в то время как клиент принимает и загружает кусок, что лучше, чем предлагать всю вещь сразу, что довольно сложно для файловой системы и весь сервер.
EDIT: Не отвечая на этот вопрос, спрашивающий спросил о чтении файла. Вот функция, которую я использую. Поставьте на него полный путь к файлу.
function readfile_chunked($file_path, $retbytes = true) { $buffer = ''; $cnt = 0; $chunksize = 1 * (1024 * 1024); // 1 = 1MB chunk size $handle = fopen($file_path, 'rb'); if ($handle === false) { return false; } while (!feof($handle)) { $buffer = fread($handle, $chunksize); echo $buffer; ob_flush(); flush(); if ($retbytes) { $cnt += strlen($buffer); } } $status = fclose($handle); if ($retbytes && $status) { return $cnt; // return num. bytes delivered like readfile() does. } return $status; }
Я пробовал разные подходы (чтение и отправка файлов небольшими фрагментами (см. Комментарии к readfile
в PHP doc], используя PEARs HTTP_Download), но я всегда сталкивался с проблемами производительности, когда файлы становятся большими.
Существует Apache mod X-Sendfile
где вы можете выполнять свою бизнес-логику, а затем делегировать загрузку в Apache. Загрузка не будет общедоступной. Я думаю, это самое элегантное решение проблемы.
Больше информации:
То же самое происходит ко мне, и я не использую сеансы. session.auto_start установлен в 0 Мой пример скрипта запускает только «sleep (5)», а добавление «session_write_close ()» в начале не решает проблему.
Проверьте файл httpd.conf. Возможно, у вас есть «KeepAlive On», и поэтому ваш второй запрос зависает до тех пор, пока первый не будет завершен. В общем, ваш PHP-скрипт не должен позволять посетителям долго ждать. Если вам нужно загрузить что-то большое, сделайте это в отдельном внутреннем запросе, который пользователь не имеет прямого контроля. До его завершения верните некоторый статус «выполнения» конечным пользователям и, когда это будет сделано, обработайте фактические результаты.