Download-Technik mit Zähler

Wie ein Download im Browser zustande kommt

Der einfache Fall ist der, dass ein Link (<a>) als "href"-Attribut die Datei zum Downloaden erhält. Ist dies keine HTML-Datei sondern z.B. etwas, das der Browser als "eher zum Herunterladen" von sich aus erkennt, (z.B. .pdf, .zip) dann bietet der Browser von sich aus den Download-Dialog an. Allerdings wird es in den letzten Jahren gerade bei PDF unklar, was der Browser dann machen sollte – mit dem Plugin als Quasi-Webseite selbst anzeigen, oder doch ein Download anbieten. Der HTML-Syntax an dieser Stelle bietet keine Möglichkeit, das gewünschte Verhalten anzugeben – weswegen eine Firefox Erweiterung nun bei PDFs von sich aus den Benutzer frägt, was er damit gemacht haben will.

Der Haken an der Geschichte ist, dass in HTML – ohne sich auf JavaScript zu verlassen – ein Benutzer-Klick immer nur zu einer Aktion führt – d.h., wenn ein Download auf dieser Weise gestartet wird, kann keine weitere Aktion im gleichen Atemzug angeleiert werden (z.B. das Zählen des Downloads). Um dieses Problem zu umgehen werden Downloads oft als "src"-Attribut eines auf der Seite versteckten Iframes eingeleitet, um z.B. das Zählen serverseitig beim Abruf der Seite zu ermöglichen (oder auch, weil die vorliegende Situation mit der Bedienerführung sich so eleganter lösen lässt).

Es gibt jedoch eine ganz andere Lösung:

Im Header einer jeden Antwort des Webservers auf einen Abruf durch den Browser gibt es eine Zeile "Content-Disposition:", die besagt, wie der Browser (UA - User Agent) die folgenden Daten darstellen soll. Siehe hierzu RFC2183. Das Standard-Verhalten ist, dass der UA den Inhalt selbst direkt anzeigen soll – "Content-Disposition: inline". Die andere Möglichkeit, "Content-Disposition: attachment" veranlasst den UA (E-Mail Client bzw. Browser) die Daten als Anhang zu behandeln. Beim E-Mail Client (MUA - Mail User Agent) wird die Datei also als Anhang angezeigt, beim Browser hingegen wird dem Benutzer der Download-Dialog ("Save as") angeboten. Dies hat im Gegensatz zur <a href> Methode den besonderen Vorteil, dass die aktuell im Browser-Fenster dargestellten Seite – von der aus der Benutzer das Download startet – die aktuelle Seite bleibt (z.B. für weitere Downloads).

Beispiel-Verwendung (aus dieser Seite)
[ HEAD, NAVIGATION ]
<?php
include("cntdown.inc.php");
?>
[ SEITEN-INHALT ]
...
<p style="margin-top:8px;">Zip (30kb) downloaden: 
 <a href="../downloads/download.php?tgt=downloads.zip"
 title="Downloadarchiv herunterladen (Zip, 30kb)"><img
 src="../images/download.gif" width="24" height="17"
 alt="Downloadarchiv herunterladen (Zip, 30kb)"
 style="vertical-align:text-bottom;" /></a></p>
<p>Abrufe bisher:
<?php echo read_dl_count('downloads.zip') ?></p>
...

Zum Herunterladen bitte einloggen.

 

Mit PHP Downloads zählen

Das vorliegende Paket enthält im Wesentlichen zwei PHP-Dateien

cntdown.inc.php (includiert logging.inc.php)

Erkennt: with login module V1 / with login module V2 / no login module

Diese Include-Datei verwaltet zwei Text-Dateien im Webspace, in denen für beliebig viele Download-Dateien je einen Zählerstand enthalten ist. Das Format ist minimalistische Format einfach – eine Zeile pro Download-Datei: <Dateiname>|<Zählerstand>.

Die eine Datei wird jede Woche mit "YYYYWW." neu angelegt und nur in der KW fortgeschrieben; die andere summiert über beliebig lange Zeiträume. Folgende Funktionen stehen hierfür zur Verfügung:

  • read_dl_count($tgt) – liefert den Stand für Datei $tgt als String zurück
  • incr_dl_count($tgt, $init='1', $type='DN') – erhöht den Stand für $tgt um 1.

Ferner gibt es eine Verwaltung zum Logging von Up-und Downloads, ebenfalls je in einer wöchentlichen Datei sowie in einer Datei der letzten "n" Ereignisse.

  • write_dl_log($tgt) – hängt einen Download-Logsatz ans Ende der Logdateien
  • write_up_log($tgt) – hängt einen Upload- oder Delete-Logsatz ans Ende der Logdateien

Beide Funktionen legen die Zähler-Datei bzw. einen neuen Eintrag an, sollte diese(r) fehlen. Die Funktion incr_dl_count erzeugt auch den neuen Logsatz, soweit dafür konfiguriert. Alle Zugriffe erfolgen mit vorbildlichem Locking – das bekannte Problem, dass flock() nicht zuverlässig funktioniert bei fopen(...,'w'), wird durch die Verwendung eigener Lockdateien vermieden.

download.php

Hierfür liegen 2 "Offerten" vor - mit Berücksichtigung meines Login-Moduls (PLM), in der Version 1 oder 2. Die für PLM V2 stolpert nicht ohne PLM, heisst dafür direkt "download.php". Sollten Sie dennoch die andere Version brauchen, benennen Sie die von Ihnen benötigte Fassung auf "download.php" um.

Diese Datei wird als Ziel eines Links aufgerufen, den der Benutzer zum Starten eines Downloads klickt. Der Name der gewünschten Datei wird im Aufruf als Get-Parameter namens "tgt" (also hinter einem Fragezeichen) angehängt, z.B.
<a href="download.php?tgt=infos.pdf">.

Ein optionaler Get-Parameter "dnm" erlaubt es, im Browser "Speichern als" Dialog einen anderen Dateinamen als auf dem Server vorschlagen zu lassen, z.B. <a href="download.php?tgt=infos.pdf&dnm=meine-infos.pdf">.

Sie includiert "cntdown.inc.php", erhöht den Zähler der gewünschten Datei durch Aufruf von incr_dl_count($tgtFile), und schickt abschließend die besonderen Header-Angaben und die abgerufene Datei an den Browser zurück.

Im Normallfall muss sich die angeforderte Datei in einem "docs" Unterverzeichnis des "downloads" Verzeichnis sich befinden. Für den Fall, dass der Name der angeforderte Datei eines der Zeichen ": / \" enthält, womit ein Herunterladen beliebiger Dateien vom Server möglich sein könnte, wird eine entsprechende Mecker-Datei (download-datei-ungueltig.txt) zurückgeliefert. Die Datei im Package können Sie nach Bedarf inhaltlich anpassen.
Ausnahme: PLM V2 wird erkannt und der User ist eingeloggt. Dann wird Zugriff auf den Benutzer- und Interessen-Verzeichnissen gewährt.

Für den Fall dass die angeforderte Datei in "docs" fehlt, wird eine entsprechende Default-Datei (download-datei-fehlt.txt) zurückgeliefert. Die Datei im Package können Sie nach Bedarf inhaltlich anpassen.

Für die gebräuchlichsten Download-Dateitypen wird "ad hoc" der richtige Mime-Type ermittelt und als Header ausgegeben. Weitere Headers zum jeweiligen Mime Type können vom Webserver dazu kommen. Für unbekannte Download-Typen wird der Mime Type "application/x-download" gesetzt, den es nicht gibt – womit ein Caching meist verhindert wird. Siehe auch meine Seite "Webtechnik / HTTP Cache".

<title>Download-Technik mit Zähler - Tim Reeves Internet-Programmierung</title> <meta name= "description" content= "Tim Reeves Internet-Programmierung - Elegantes Herunterladen von Dateien in PHP - mit Zähler" /> <meta name= "keywords" content= "Download, Zähler, Content-Disposition" /> <meta name= "date" content= "2010-04-08" /> <?php include("../head.inc.php"); include("../navi.inc.php"); include("../cntdown.inc.php"); ?> <div class= "content"> <table cellpadding= "0" cellspacing= "0" class= "spalten"> <tr> <td class= "sp5050"> <h1 style= "margin-top:12px">Download-Technik mit Zähler</h1> <h5>Wie ein Download im Browser zustande kommt</h5> <p>Der einfache Fall ist der, dass ein Link (&lt;a&gt;) als "href"-Attribut die Datei zum Downloaden erhält. Ist dies keine HTML-Datei sondern z.B. etwas, das der Browser als "eher zum Herunterladen" von sich aus erkennt, (z.B. .pdf, .zip) dann bietet der Browser von sich aus den Download-Dialog an. Allerdings wird es in den letzten Jahren gerade bei PDF unklar, was der Browser dann machen sollte &ndash; mit dem Plugin als Quasi-Webseite selbst anzeigen, oder doch ein Download anbieten. Der HTML-Syntax an dieser Stelle bietet keine Möglichkeit, das gewünschte Verhalten anzugeben &ndash; weswegen eine Firefox Erweiterung nun bei PDFs von sich aus den Benutzer frägt, was er damit gemacht haben will.</p> <p class= "mehrluft">Der Haken an der Geschichte ist, dass in HTML &ndash; ohne sich auf JavaScript zu verlassen &ndash; ein Benutzer-Klick immer nur zu einer Aktion führt &ndash; d.h., wenn ein Download auf dieser Weise gestartet wird, kann keine weitere Aktion im gleichen Atemzug angeleiert werden (z.B. das Zählen des Downloads). Um dieses Problem zu umgehen werden Downloads oft als "src"-Attribut eines auf der Seite versteckten Iframes eingeleitet, um z.B. das Zählen serverseitig beim Abruf der Seite zu ermöglichen (oder auch, weil die vorliegende Situation mit der Bedienerführung sich so eleganter lösen lässt).</p> <h5>Es gibt jedoch eine ganz andere Lösung:</h5>