У меня есть простая служба загрузки файлов, написанная на PHP, которая также включает скрипт, который контролирует скорость загрузки, отправляя пакеты с ограниченным размером, когда пользователь запрашивает загрузку с этого сайта.
Я хочу внедрить систему для ограничения параллельной / одновременной загрузки до 1 на пользователя, если они не являются участниками премии. В вышеприведенном сценарии загрузки я могу использовать базу данных MySQL для хранения записи, которая имеет: (1) идентификатор пользователя; (2) идентификатор файла; (3) при запуске загрузки; и (4), когда был отправлен последний пакет, который обновляется каждый раз, когда это делается (если скорость DL ограничена 150 кБ / с, то после каждых 150 кБ эта запись обновляется и т. д.).
Однако до сих пор запись базы данных будет удалена только после того, как загрузка будет успешно завершена – в конце сценария после полной загрузки загрузка записи удаляется из таблицы:
insert DB record; while (download is being served) { serve packet of data; update DB record with current date/time; } // Download is now complete delete DB record;
Как я смогу обнаружить, когда загрузка была отменена? Должен ли я просто иметь задание Cron (или что-то подобное), обнаруживать, если существующая запись загрузки больше, чем X минут / часов? Или есть что-то еще, что я могу сделать, чего не хватает?
Надеюсь, я объяснил это достаточно хорошо. Я не думаю, что требуется отправить конкретный код; Меня больше интересует логистика того, как это сделать. Если это необходимо, я с радостью предоставил его.
ПРИМЕЧАНИЕ. Я знаю, как определить, был ли файл загружен успешно; Мне нужно знать, как определить, был ли он отменен, прерван или иным образом остановлен (а не просто приостановлен). Это будет полезно для остановки параллельных загрузок, а также для предотвращения ситуации, когда пользователь отменяет Download # 1 и пытается инициировать загрузку # 2, только чтобы найти, что сайт утверждает, что он все еще загружает файл # 1.
EDIT: здесь вы можете найти скрипт загрузки: http://codetidy.com/1319/ – он поддерживает многопользовательские загрузки и возобновляет загрузку.
<?php class DownloadObserver { protected $file; public function __construct($file) { $this->file = $file; } public function send() { // -> note in DB you've started readfile($this->file); } public function __destruct() { // download is done, either completed or aborted $aborted = connection_aborted(); // -> note in DB } } $dl = new DownloadObserver("/tmp/whatever"); $dl->send();
должен работать нормально. Нет необходимости в shutdown_function или любом фанк-самодельном наблюдении за соединением.
Вы хотите проверить следующие функции: connection_status (), connection_aborted () и ignore_user_abort () (дополнительную информацию см. В разделе обработки подключений в руководстве по PHP).
Хотя я не могу гарантировать надежность (прошло некоторое время с тех пор, как я играл с ней), с правильной комбинацией вы сможете выполнить то, что хотите. Есть несколько предостережений при работе с ними, хотя большая из них заключается в том, что если что-то пойдет не так, вы можете столкнуться с скрученными скриптами PHP, запущенными на сервере, требуя, чтобы вы убили Apache, чтобы остановить их.
Следующее должно дать вам хорошее представление о том, как это сделать (адаптировано из примеров кода PHP и нескольких комментариев):
<?php //Set PHP not to cancel execution if the connection is aborted //and drop the time limit to allow for big file downloads ignore_user_abort(true); set_time_limit(0); while(true){ //See the ignore_user_abort() docs re having to send data echo chr(0); //Make sure the data gets flushed properly or the connection check won't work flush(); ob_flush(); //Check then connection status and exit loop if aborted if(connection_status() != CONNECTION_NORMAL || connection_aborted()) break; //Just to provide some spacing in this example sleep(1); } file_put_contents("abort.txt", "aborted\n", FILE_APPEND); //Never hurts to ensure that the script halts execution die();
в<?php //Set PHP not to cancel execution if the connection is aborted //and drop the time limit to allow for big file downloads ignore_user_abort(true); set_time_limit(0); while(true){ //See the ignore_user_abort() docs re having to send data echo chr(0); //Make sure the data gets flushed properly or the connection check won't work flush(); ob_flush(); //Check then connection status and exit loop if aborted if(connection_status() != CONNECTION_NORMAL || connection_aborted()) break; //Just to provide some spacing in this example sleep(1); } file_put_contents("abort.txt", "aborted\n", FILE_APPEND); //Never hurts to ensure that the script halts execution die();
Очевидно, что для того, как вы будете использовать его, отправляемые данные будут просто частью загружаемых данных (просто убедитесь, что вы правильно сбросили буфер, чтобы обеспечить фактическое отправление данных). Насколько мне известно, нет никакого способа сделать различие между паузой и прерыванием / остановкой. Функция паузы / возобновления (и многопользовательская загрузка – то есть, как диспетчеры загрузки ускоряют загрузку) полагается на заголовок «Range», в основном запрашивая байт x для байта y файла. Поэтому, если вы хотите разрешить возобновление загрузки, вам также придется иметь дело с этим.
Сигнал «отменить» HTTP не отправляется по умолчанию. Таким образом, похоже, вам нужно будет решить вопрос о тайм-ауте, время, в течение которого соединение может сидеть без отправки / получения другого пакета. Если вы отправляете довольно мелкие пакеты (как я полагаю, вы так), удерживайте тайм-аут коротким для лучшего эффекта.
В текущем состоянии вам нужно будет проверить возраст последнего временного обновления, если он слишком старый, прекратите отправку файла.