Sooo.. hab nun endlich Zeit gehabt, mal nen Download-Counter zu programmieren:
Kopierst du folgenden Code z.B. nach
http://winhistory.de/downloads/ als Datei
getfile.php, kannst du die Downloads z.B. über
http://winhistory.de/downloads/getfile.php/DOS5.exe verlinken. Ich hab das absichtlich nicht über Parameter implementiert, weil da einige Browser/Downloadmanager dann einen falschen Dateinamen setzen (Ansonsten einfach das Argument von
download_file() abändern).
Existiert die Datei
http://winhistory.de/downloads/dateien/DOS5.exe, dann zählt er den Counter für die Datei einen hoch und leitet dann den Browser um (geht mit jedem HTTP/1.1-konformen Browser/Downloadmanager/Telnetbenutzer *narf*). Existiert die Datei nicht, zählt er nichts, leitet aber trotzdem weiter (gibt dafür dann die Standard 404 deines Webservers aus).
Die ersten drei Konstanten kannst du evtl. noch anpassen. Wenn du willst, kannst du die counter.txt auch ausserhalb des HTTP-Zugriffes ablegen (einfach Pfad ändern), damit von aussen keiner die Counter lesen kann (kaputt machen kann man damit allerdings eigentlich nichts).
PHP-Code:
<?php
/* Lokaler Pfad in dem nach Dateien gesucht wird */
define("FS_DIR", "dateien/");
/* HTTP-Pfad zum obigen Ordner */
define("HTTP_DIR", "http://winhistory.de/downloads/dateien/");
/* Counter-Datei für alle Dateien */
define("COUNTER_FILE", "counter.txt");
function read_counter_file() {
$counter_array = array();
if($fp = @fopen(COUNTER_FILE, "r")) {
flock($fp, LOCK_SH);
while(!feof($fp)) {
$buffer = trim(fgets($fp, 256));
if(empty($buffer)) continue;
list($file, $count) = explode('|', $buffer, 2);
$counter_array[$file] = (int) $count;
}
fclose($fp);
}
return $counter_array;
}
function write_counter_file(array $counter_array) {
if($fp = @fopen(COUNTER_FILE, "w")) {
flock($fp, LOCK_EX);
foreach($counter_array as $file => $count) {
fwrite($fp, $file.'|'.$count.PHP_EOL);
}
fflush($fp);
fclose($fp);
return true;
}
return false;
}
function get_counter_for($file) {
$counter_array = read_counter_file();
return isset($counter_array[$file]) ?
$counter_array[$file] : 0;
}
function download_file($requested_file) {
/* die Anfrage überprüfen */
if(empty($requested_file) || $requested_file == "/") {
header("HTTP/1.1 400 Bad Request");
exit("<h1>400 Bad Request</h1>");
}
/* überflüssiges '/' aus dem String entfernen */
$requested_file = substr($requested_file, 1);
/* wenn die Datei existiert, den Counter hochzählen */
if(file_exists(FS_DIR.$requested_file)) {
$counter_array = read_counter_file();
if(isset($counter_array[$requested_file])) {
$counter_array[$requested_file]++;
} else {
$counter_array[$requested_file] = 1;;
}
ignore_user_abort(1);
write_counter_file($counter_array);
ignore_user_abort(0);
}
header("HTTP/1.1 302 Found");
header("Location: ".HTTP_DIR.$requested_file);
exit();
}
/* überprüfen, ob die Datei inkludiert wurde */
list($main_file) = get_included_files();
/* wenn nicht, dann die Weiterleitung starten */
if($main_file == __FILE__) {
$requested_file =
str_replace($_SERVER['SCRIPT_NAME'], "", $_SERVER['PHP_SELF']);
download_file($requested_file);
}
?>
Ich hoffe, das ganze ist Race-Condition sicher. Ich arbeite zwar mit Dateisperren, aber so ganz trau ich PHP in der Hinsicht nicht. Die Zählerdatei ging bei mir bei ein paar Stress-Tests allerdings nicht in die Brüche.
Natürlich hab ich auch grad eine Programmierschnittstelle eingebaut. Wenn du die Datei nicht via Browser aufrufst, sondern via include() einbindest, kannst du auf die Funktionen zugreifen, ohne dass er weiterleiten will. Beispiel:
PHP-Code:
<?php
include("getfile.php");
/* gibt den Wert des Counter der Datei DOS5.exe zurück */
echo get_counter_for("DOS5.exe");
?>