通过PHP下载主机文件、日志和主机统计信息


Host file, log download, and host stats via PHP?

我是一个。net的家伙,在这里尝试PHP的事情,所以我现在完全离开了我的舒适区。我想我要做的是有3个文件:

  • download.php :

    (a)包含对文件名的id查找(因此download.php?file=11 querystring告诉我应该托管abc.zip)

    (b)记录下载到stats.log的代码

    (c)一对header()呼叫和一个readfile()呼叫,类似于这个问题的答案

  • stats.log :一个简单的日志文件,可能看起来像下面的例子。这允许通过简单地附加一行文本来完成日志记录,并且允许我不时地压缩它。


abc.zip 1234
xyz.zip 4321
abc.zip 1
abc.zip 1
abc.zip 1
xyz.zip 1
abc.zip 1

  • stats.php :这是最终提供统计的PHP文件。它们可以是实时的或接近实时的,可能每分钟重新读取文件并缓存它或其他。我真的不在乎,这不会经常被击中,但我需要确保这不是一个愚蠢的昂贵的操作。这页不需要很漂亮。重要的是要让人容易读懂,所以没有特别的要求。对于上面的stats.log示例,我希望它提供如下内容:

abc.zip: 1238 downloads
xyz.zip: 4322 downloads

最终,我不希望数据库或任何其他系统参与其中。我只有FTP访问服务器的权限,所以除了将脚本放入目录之外,我不能做更多的事情。我意识到我需要确保脚本具有对stats.txt的写权限,这很好。

我的问题。我有很多,但我相信对于了解PHP的人来说,它们都很容易。

  1. 我想我通过设置头和使用readfile来理解download.php的托管部分。然而,我怎么能有一个键/值对表示文件id和文件名的集合?如果我在。net中,我可以这样做:var foo = new Dictionary<int, string> {{11, "abc.zip"}, {12, "xyz.zip"}},但我不知道这在PHP中是什么样子的。
  2. 我如何获得查询字符串?我需要从URL "stuff/download.php? "文件=11",并采取11从我的查找集合中抓取我的"abc.zip"。
  3. 我如何将换行符写入我的stats.log文件?
  4. 我如何在我的stats.php脚本中循环我的stats.log文件来计算和托管这些统计数据?
  5. 附加问题:我如何缓存从步骤4的结果,只读取文件每分钟/小时/或什么?
如果有人能回答这些问题中的至少大部分,我可能会填补一些空白,但帮助肯定会很感激!:)

1-您正在寻找array,例如

 $files=array(11=>'abc.zip',
             12=>'xyz.zip');

2-查询字符串被超全局$_GET访问,所以在你的例子中$_GET['file']保存着你感兴趣的数据。

3、4、5所示我建议存储JSON编码的信息。例如

$rawInfo=file_get_contents('stats.log');
$Info=json_decode($rawInfo,true);
if(isset($Info[$_GET['file']])){
    $Info[$_GET['file']]++;
}else{
    $Info[$_GET['file']]=1;
}
$rawInfo=json_encode($Info);
$h=fopen('stats.log','c');// If $h is false, you couldn't open the file
flock($h,LOCK_EX);// lock the file
$b=fwrite($h,$rawInfo);// if $b is not greater than 0, nothing was written
flock($h,LOCK_UN);
fclose($h);
//And then actually serve the file requested

这样做的好处是已经将信息以一种有用的格式存储了。

无论何时取出json_decode ed数据,它都是array的格式,您需要知道如何处理。

stats.php可能像这样:

$rStats=file_get_contents('stats.log');
$Stats=json_decode($rStats,true);
foreach($Stats as $k=>$v){
   echo $k.': '.$v.' download'.($v==1?'':'s');
}

如果没有@Shad的帮助,我永远不会这么容易地完成这个被接受的答案。因此,我想在这里发布我的最终解决方案,应该适用于几乎所有人。这允许"直接链接"(即没有301/302或其他类型的重定向)正常工作(右键单击-> save as works too),同时仍然记录下载。注意,这是相当"资源繁重"的,一些共享主机可能会对使用这样的东西感到不安,但据我所知,这应该不是一个主要的消耗。我将托管的文件大约是3-15MB,不会有大量的下载,所以我不太担心这个问题,但如果你使用这个解决方案,请非常注意这个事实!

download.php :

<?php
$fileLookup = array(
            0=>'subdir/test.zip',
            1=>'another/sub/dir/test2.zip'
        );
$currentRelativeFileName = $fileLookup[$_GET['file']];
$currentFileName = basename($currentRelativeFileName);
$rawInfo=file_get_contents('stats.log');
$Info=json_decode($rawInfo,true);
if(isset($Info[$currentFileName])){
    $Info[$currentFileName]++;
}else{
    $Info[$currentFileName]=1;
}
$rawInfo=json_encode($Info);
$h=fopen('stats.log','c');// If $h is false, you couldn't open the file
flock($h,LOCK_EX);// lock the file
$b=fwrite($h,$rawInfo);// if $b is not greater than 0, nothing was written
flock($h,LOCK_UN);
fclose($h);
header("Content-type: application/octet-stream"); 
header("Content-disposition: attachment; filename=" . $currentFileName); 
readfile($currentRelativeFileName);
?>

stats.php :

<html>
<head>
    <title>Here are my stats!</title>
</head>
<body>
<?php
$rStats=file_get_contents('stats.log');
if (strlen($rStats) > 5){
    $Stats=json_decode($rStats,true);
    foreach($Stats as $k=>$v){
       echo $k.': '.$v.' download'.($v==1?'':'s') . '<br />';
    }
}else{
    echo 'No downloads';
}
?>
</body>