首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >获取由readfile()引起的流量

获取由readfile()引起的流量
EN

Stack Overflow用户
提问于 2011-02-27 00:16:18
回答 1查看 834关注 0票数 2

我使用readfile让客户端通过我的服务器下载文件。因此,我将从readfile('external-url')接收到的数据直接输出到客户端。

现在我想确定由readfile()引起的流量。

我可以通过readfile的返回值来确定它,但前提是客户端完成下载。否则脚本将停止工作,并且readfile()的返回值为0。

首先,我尝试了以下代码:

代码语言:javascript
复制
//outputs download headers
//creating $stream_context with request headers for the external download server
$traffic = readfile($url, false, $stream_context);
//save traffic...

当客户端停止下载时,从不调用保存通信。

然后,我使用register_shutdown_function()注册了一个关闭函数,其中包含$traffic作为全局变量来保存流量。现在创建了traffic-file,但使用的流量为0。

我没有访问服务器日志或其他东西的权限。我只能使用php和htaccess。

我现在使用的一个变通方法是启动对文件的请求,解析文件大小,并将完整的文件大小添加到客户端流量中。然后,我使用readfile()开始下载。如果客户端停止下载,则会像下载整个文件一样进行处理。

第三种方法可以是curl及其CURLOPT_WRITEFUNCTION-settings。但这对服务器来说开销太大,与我想做的事情无关:保存真正的流量。

在下载文件之前保存客户端流量还有另一个问题:我希望支持恢复和分块下载(多个连接到一个文件以加快下载速度)。这仍然有效,但问题是计算流量!对于块,我可以解析HTTP-RANGE报头来确定请求的文件部分,并将其保存为流量,但是恢复又如何呢?

那么,世界上有没有可能的解决方案呢?

我仍然不使用数据库,我只使用一个带有htaccess -logininformation的文件来识别客户端,并将每个客户端使用的流量保存在我的网站空间上的一个单独的文件中。

下面是我的代码:

代码语言:javascript
复制
//$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);

现在来看一下函数:

代码语言:javascript
复制
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;
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2011-02-28 04:17:58

我在想curl是如何实现保存到文件流的功能的。我意识到它必须是一个特殊的CURLOPT_WRITEFUNCTION,因为在保存到文件流的同时停止脚本会在我的has空间中留下一个包含已经加载的部分的文件。

因此,我用CURLOPT_WRITEFUNCTION尝试了一下,它似乎并不像我想象的那样资源密集。

现在,我使用register_shutdown_function来调用一个函数,该函数可以保存使用的流量。我的CURLOPT_WRITEFUNCTION计算加载的数据=流量。

如果想要将流量保存在文件中,将当前工作目录存储在变量中也很重要。因为在已注册的关闭函数中调用的每个相对路径都不是相对于根目录的,所以它是相对于服务器根目录的!您也可以使用绝对路径而不是cwd。

代码语言:javascript
复制
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);

谢谢你的帮助!它非常有用!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5128052

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档