我使用 readfile 让客户端通过我的服务器下载文件。因此,我将从 readfile('external-url') 接收到的数据直接输出到客户端。
现在我想确定由 readfile() 引起的流量。
我可以通过 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...
当客户端停止下载时,从未调用保存流量。
然后我用 register_shutdown_function() 注册了一个关闭函数,其中包括 $traffic 作为全局变量来保存流量。现在创建了流量文件,但使用的流量为 0。
我无权访问服务器日志或其他内容。我只能使用 php 和 htaccess。
我现在使用的一种解决方法是启动对文件的请求,解析文件大小并将完整的文件大小添加到客户端流量中。然后我用 readfile() 开始下载。如果客户端停止下载,则会像下载整个文件一样处理它。
第三种方法可能是 curl 及其 CURLOPT_WRITEFUNCTION 设置。但这对服务器来说开销太大,与我想做的事情无关:保存真正的流量。
在下载文件之前保存客户端流量还有另一个问题:我想支持恢复和分块下载(多个连接到一个文件以加快下载速度)。这仍然有效,但问题是计算流量!对于块,我可以解析 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;
}