получить трафик, вызванный readfile () - PullRequest
2 голосов
/ 26 февраля 2011

Я использую readfile, чтобы позволить клиенту загрузить файл через мой сервер.Для этого я вывожу данные, которые я получаю из readfile ('external-url'), прямо на клиент.

Теперь я хочу определить трафик, который вызывается readfile ().

Iможет определить его по возвращаемому значению readfile, но только если клиент заканчивает загрузку.В противном случае скрипт перестает работать, и возвращаемое значение readfile () равно 0.

Сначала я попробовал этот код:

//outputs download headers
//creating $stream_context with request headers for the external download server
$traffic = readfile($url, false, $stream_context);
//save traffic...

Сохранение трафика никогда не вызывалось, когда клиент прекращал загрузку.

Затем я зарегистрировал функцию shutdown с помощью register_shutdown_function (), которая включала $ traffic в качестве глобальной переменной для сохранения трафика.Теперь файл трафика был создан, но использовался трафик 0.

У меня нет доступа к журналам сервера или что-то еще.Я могу использовать только php и htaccess.

Один из способов, который я сейчас использую, заключается в том, что я запускаю запрос к файлу, анализирую размер файла и добавляю полный размер файла в трафик клиента.Затем я начинаю загрузку с readfile ().Если клиент прекращает загрузку, он обрабатывается так, как если бы он загружал весь файл.

Третий метод может быть curl и его CURLOPT_WRITEFUNCTION-settings.Но это слишком много для сервера и не имеет никакого отношения к тому, что я хочу сделать: сохранить реальный трафик.

Существует также другая проблема с сохранением трафика клиента перед загрузкой файла: я хочуподдержка возобновления и частичной загрузки (несколько подключений к одному файлу для более быстрой загрузки).Это все еще работает, но проблема в подсчете трафика!Для кусков я могу разобрать заголовок HTTP-RANGE, чтобы определить запрошенные части файла и сохранить его как трафик, но как насчет возобновления?

Так есть ли какое-нибудь возможное решение в мире?

Я до сих пор не использую базу данных, я использую только файл с htaccess -logininformation для идентификации клиентов и сохранения использованного трафика для каждого клиента в отдельном файле на моем веб-пространстве.

Вот также мой код:

//$download = array(url, filesize, filename) got it whith a separate curl request to the external file
$downloadHeader = CreateDownloadHeaders($download, $_hoster->AcceptRanges());

$requestOptions = array(
    'http'=>array(
        'method' => 'GET',
        'header' => CreateRequestHeaders($download['filesize'], $_hoster->AcceptRanges())
    )
);

$requestOptions['http']['header'] = array_merge($requestOptions['http']['header'], $_hoster->GetAdditionalHeaders());

//Output download headers for our client
foreach($downloadHeader as $header) {
    header($header);
}


register_shutdown_function('SaveTraffic', $username, $givenUrl, $download['filename'], $download['filesize']);
//SaveTraffic($username, $givenUrl, $download['filename'], $download['filesize']);

$context = stream_context_create($requestOptions);
$traffic = readfile($download['url'], false, $context);

А теперь функции:

function CreateDownloadHeaders($download, $acceptRanges) {
    //IE workaround for downloads
    $type = (isset($_SERVER['HTTP_USER_AGENT']) && strpos($_SERVER['HTTP_USER_AGENT'],'MSIE')) ? 'force-download' : 'octet-stream';

    $headers = array(
        'Content-Type: application/' . $type,
        'Content-Disposition: attachment; filename="'.$download['filename'].'"',
        'Content-Length: '.$download['filesize'],
        'Content-Transfer-Encoding: Binary',
        'Expires: 0',
        'Cache-Control: must-revalidate, post-check=0, pre-check=0',
        'Pragma: public',
        'Connection: close'
    );

    $headers = AddDownloadRangeHeaders($headers, $acceptRanges, $download['filesize']);

    return $headers;
}


function CreateRequestHeaders($filesize, $acceptRanges) {
    $headers = array();

    $headers = AddRequestRangeHeaders($headers, $acceptRanges, $filesize);
    $headers[] = 'User-Agent: Mozilla/5.0 (Windows; U; Windows NT 6.1; de; rv:1.9.2.13) Gecko/20101203 Firefox/3.6.13';
    $headers[] = 'Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8';
    $headers[] = 'Accept-Language: de, en-gb;q=0.9, en;q=0.8';
    $headers[] = 'Accept-Encoding: gzip, deflate';
    $headers[] = 'Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7';
    $headers[] = 'Cache-Control: no-cache';
    $headers[] = 'Pragma: no-cache';
    $headers[] = 'Connection: close';

    return $headers;
}


function AddDownloadRangeHeaders($headers, $acceptRanges, $filesize) {
    if($acceptRanges !== true) {
        $headers[] = 'Accept-Ranges: none';
    }
    elseif(isset($_SERVER['HTTP_RANGE'])) {
        preg_match('/bytes([[:space:]])?=([[:space:]])?(\d+)?-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);

        $start = intval($matches[3]);
        $stop = intval($matches[4]);

        if($stop == 0) {
            $stop = $filesize;
        }

        $headers[] = 'HTTP/1.1 206 Partial Content';
        $headers[] = 'Accept-Ranges: bytes';
        $headers[] = 'Content-Range: bytes ' . $start . '-' . $stop . '/' . $filesize;

        $newSize = $stop - $start + 1;
        $key = array_search('Content-Length: '.$filesize, $headers);
        $headers[$key] = 'Content-Length: '.$newSize;
    }

    return $headers;
}


function AddRequestRangeHeaders($headers, $acceptRanges, $filesize) {
    if($acceptRanges === true && isset($_SERVER['HTTP_RANGE'])) {
        preg_match('/bytes([[:space:]])?=([[:space:]])?(\d+)?-(\d+)?/', $_SERVER['HTTP_RANGE'], $matches);

        $start = intval($matches[3]);
        $stop = intval($matches[4]);

        if($stop == 0) {
            $stop = $filesize;
        }

        $headers[] = 'Range: bytes='.$start.'-'.$stop;
    }

    return $headers;
}

1 Ответ

1 голос
/ 27 февраля 2011

Я думал о том, как в curl реализована функция сохранения в файл потока. Я понял, что это должно быть что-то вроде специального CURLOPT_WRITEFUNCTION, потому что остановка скрипта при сохранении в файловом потоке оставляет файл в моем веб-пространстве, который содержит уже загруженную часть.

Поэтому я попробовал это с CURLOPT_WRITEFUNCTION, и это, кажется, не так ресурсоемко, как я думал.

Теперь я использую register_shutdown_function для вызова функции, которая сохраняет использованный трафик. Мой CURLOPT_WRITEFUNCTION считает загруженные данные = трафик.

Также важно сохранить текущий рабочий каталог в переменной, если вы хотите сохранить трафик в файле. Поскольку каждый относительный путь, вызываемый в зарегистрированной функции завершения работы, не относится к вашему корневому каталогу, он относится к корневому каталогу сервера! Вы также можете использовать абсолютные пути вместо cwd.

function readResponse($ch, $data) {
    global $traffic;

    $length = mb_strlen($data, '8bit');

    //count traffic
    $traffic += $length;
    //output loaded data
    echo $data;

    return $length;
}

function saveTraffic($username, $cwd) {
    global $traffic;

    $h = fopen($cwd.'/relative-path/traffic_'.$username.'.txt', 'ab');
    fwrite($h, $traffic);
    fclose($h);
}

//...

$cwd = getcwd();
register_shutdown_function('saveTraffic', $username, $cwd);

$traffic = 0;

//...
//Output download header information to client
//...

curl_setopt($ch, CURLOPT_FAILONERROR, true);
curl_setopt($ch, CURLOPT_BINARYTRANSFER, true);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_WRITEFUNCTION, 'readResponse');

//...

curl_exec($ch);

Спасибо за вашу помощь! Это было очень полезно!

...