Как ограничить скорость загрузки в этом PHP-коде

#php #download #rate-limiting #download-manager

#php #Скачать #ограничение скорости #менеджер загрузок

Вопрос:

У меня есть следующий PHP-код, который касается методов загрузки для моего веб-сайта. Я пытаюсь ограничить скорость загрузки чем-то конкретным, поскольку в настоящее время менеджеры загрузки злоупотребляют этим.

К сожалению, у меня нет опыта в программировании.

 public function download(
    $file,
    $filename,
    $file_size,
    $content_type,
    $disposition = 'inline',
    $android = false
) {
    // Gzip enabled may set the wrong file size.
    if (function_exists('apache_setenv')) {
        @apache_setenv('no-gzip', 1);
    }
    if (ini_get('zlib.output_compression')) {
        @ini_set('zlib.output_compression', 'Off');
    }
    @set_time_limit(0);
    session_write_close();
    header("Content-Length: ".$file_size);

    if ($android) {
        header("Content-Type: application/octet-stream");
        header("Content-Disposition: attachment; filename="".$filename.""");
    } else {
        header("Content-Type: $content_type");
        header("Content-Disposition: $disposition; filename="".$filename.""");
        header("Content-Transfer-Encoding: binary");
        header("Expires: -1");
    }
    if (ob_get_level()) {
        ob_end_clean();
    }
    readfile($file);
    return true;
}
  

Комментарии:

1. Вы должны заменить свой readfile циклом, управляемым таймером

2. Вместо того, чтобы делать это через PHP, разве вы не можете сделать это через веб-сервер?

3. Вы используете apache или nginx? Если nginx, то прочитайте это docs.nginx.com/nginx/admin-guide/security-controls /…

4. Вы также могли бы создать целевую страницу, где вы генерируете «одноразовый» csrf-токен, который вы передаете вместе с имеющимся у вас кодом загрузки. Если токен недействителен, то они не смогут загрузить файл. Таким образом, пользователи должны посетить целевую страницу, прежде чем они смогут что-либо загрузить, но вы не будете регулировать скорость загрузки для законных загрузок.

5. Это не сайт подрядчика, а сайт вопросов и ответов для разработчиков — если вам нужен подрядчик, попробуйте mechanical turk

Ответ №1:

Код:

 <?php 
public function download(
    $file,
    $filename,
    $file_size,
    $content_type,
    $disposition = 'inline',
    $android = false
) {
    // Gzip enabled may set the wrong file size.
    if (function_exists('apache_setenv')) {
        @apache_setenv('no-gzip', 1);
    }
    if (ini_get('zlib.output_compression')) {
        @ini_set('zlib.output_compression', 'Off');
    }
    @set_time_limit(0);
    session_write_close();
    header("Content-Length: ".$file_size);

    if ($android) {
        header("Content-Type: application/octet-stream");
        header("Content-Disposition: attachment; filename="".$filename.""");
    } else {
        header("Content-Type: $content_type");
        header("Content-Disposition: $disposition; filename="".$filename.""");
        header("Content-Transfer-Encoding: binary");
        header("Expires: -1");
    }
    if (ob_get_level()) {
        ob_end_clean();
    }

    $this->readSlow($file, 1);

    return true;
}

private function readSlow(string $filename, int $sleepAmount = 0, int $chunkSize = 409600)
{
    $handle = fopen($filename, "r");

    while (!feof($handle)) {
        echo fread($handle, $chunkSize);
        ob_flush();
        sleep($sleepAmount);
    }
    fclose($handle);
}
  

Примечание:

Я предполагаю, что вы опубликовали часть своего класса. Если вы хотите использовать readSlow() внешний класс, тогда вы используете его таким образом:

 readSlow('pathToFile', $amountOfSleep, $amountOfBytesToReadAtOneRead);
  

Это необязательно,

 ob_flush();
  

Я сделал это так, чтобы после считывания буфера он немедленно отправлялся клиенту, который запросил данные, но в вашем случае, возможно, было бы лучше удалить его — проверьте, лучше с ним или нет для разных значений $amountSleep и $chunkSize

sleep() не использует время общего выполнения вашего скрипта, поэтому, если файл загружается без какого-либо ожидания в течение 10 секунд, а ваше максимальное время выполнения php-скрипта составляет 30 секунд, то вы можете легко заставить скрипт работать 120 секунд за загрузку, используя sleep.
Вместо использования sleep () вы можете использовать usleep (), что должно дать вам более точный контроль над ограничением скорости, потому что время ожидания измеряется микросекундами, а не секундами, поэтому имейте это в виду при его использовании, и если вы хотите переходить в спящий режим между чтениями в течение 1 секунды, то $sleepAmount должно быть установлено значение 1000000 при использовании usleep()

$chunkSize это количество байтов, которое будет прочитано, а затем ваш скрипт перейдет в спящий режим. Поиграйте с этим значением, чтобы получить оптимальную скорость.

Я не тестировал код, поэтому он может работать, а может и нет, но, по крайней мере, это идея для начала.