Ограничение параллельных/одновременных загрузок - Как узнать, была ли загрузка отменена?


У меня есть простая служба загрузки файлов, написанная на 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 минут/часов? Или есть что-то еще, что я могу сделать, чего мне не хватает?

Надеюсь, я объяснил это достаточно хорошо. Я не думаю, что требуется публиковать конкретный код; Меня больше интересует логистика того, как/можно ли это сделать. Если потребуется конкретика, я с радостью ее предоставлю.

ПРИМЕЧАНИЕ: Я знаю, как определить, был ли файл успешно загружен; Мне нужно знать, как определить, был ли он был отменен, прерван или иным образом остановлен (а не просто приостановлен). Это будет полезно для остановки параллельных загрузок, а также для предотвращения ситуации, когда пользователь отменяет загрузку #1 и пытается запустить загрузку #2, только чтобы обнаружить, что сайт утверждает, что он все еще загружает файл #1.

РЕДАКТИРОВАТЬ: Вы можете найти мой скрипт загрузки здесь: http://codetidy.com/1319 / - он уже поддерживает загрузку нескольких частей и возобновление загрузки.

Author: Tshepang, 2011-10-23

3 answers

<?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 или в каком-либо фанковом наблюдении за самостоятельным подключением.

 4
Author: rodneyrehm, 2011-10-23 17:05:09

Вы захотите проверить следующие функции: 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();

Очевидно, что для того, как вы будете его использовать, отправляемые данные будут просто фрагментом загружаемых данных (просто убедитесь, что вы правильно очистили буфер, чтобы убедиться, что данные действительно отправлены). Насколько мне известно, нет никакого способа сделать различие между приостановкой и прерыванием/остановкой. Функция приостановки/возобновления (и загрузка из нескольких частей - т. Е. Как менеджеры загрузки ускоряют загрузку) зависит от заголовка "Диапазон", в основном запрашивающего байт от x до байта y файла. Поэтому, если вы хотите разрешить возобновляемые загрузки, вам тоже придется иметь дело с этим.

 1
Author: mwinnard, 2011-10-23 10:17:49

По умолчанию не отправляется HTTP-сигнал "отмена". Итак, похоже, вам нужно будет выбрать тайм-аут, время, в течение которого соединение может оставаться без отправки/получения другого пакета. Если вы отправляете довольно небольшие пакеты (как я предполагаю), держите время ожидания коротким для достижения наилучшего эффекта.

В вашем состоянии while вам нужно будет проверить возраст последнего обновления метки времени, если оно слишком старое, прекратите отправку файла.

 0
Author: ctcherry, 2011-10-23 04:53:32