Downloadgeschwindigkeit von Dateien begrenzen

Bewertung [ Bewertung abgeben ] Artikel geschrieben am 14.11.2022 um 23:13 Uhr, aktualisiert am 22.07.2023, um 21:48 Uhr.

Wir sehen es bei vielen Webseiten, dass entweder die Downloadgeschwindigkeit künstlich gedrosselt wird oder der Server schlicht nicht mehr liefern kann. Falls der Server keine Begrenzung besitzt und zugleich der Inhalt sehr beliebt ist, dann bleibt einem oft nur die Möglichkeit außerhalb der Hauptzeiten die Downloads zu starten.

Auch als Serverbetreiber kann es unterschiedliche Gründe geben, weshalb eine Drosselung durchaus in Frage kommt. In den meisten Fällen wird z.B. der öffentliche Download beschränkt und mit einem entsprechenden Premium-Account ist der Zugriff nahezu unbegrenzt möglich.

Oft schon werden Dateien auf einem Webspace nicht mehr direkt verlinkt sondern mindestens durch ein kleines PHP-Skript geschleust, um zusätzliche Informationen zu sammeln und sei es nur um einen Download-Zähler zu erhöhen. Dieses Skript lässt sich sehr leicht modifizieren und damit die Download-Geschwindigkeit des Clients drosseln.
In den meisten PHP-Dateien wird dafür auf die Funktion readfile() zurückgegriffen. Alternativ kommt gerne auch ein fopen(), inklusive einer while-Schleife zum Einsatz, die mit fread() einfach einen Teil des Dateiinhalts ausliest und per echo an den Browser weiterleitet. Alle Wege erfüllen ihren jeweiligen Zweck. Welche Methode die Bessere ist entscheidet meist das Einsatzgebiet. In meinem Fall liegen die Daten als Binärdaten in einer Datenbank, dafür sind die Datenmengen sehr überschaubar. Möchte man deutlich größere Dateien im Download beschränken, dann ist die Wahl zu fopen() durchaus vorteilhaft für das Speichermanagement.

Die grundsätzliche Idee ist sehr einfach. Während wir die Datei zum Browser senden werden wir in jeder Iteration eine kleine Pause einlegen. Mithilfe der Länge der Pause lässt sich am Ende die Download-Geschwindigkeit errechnen. Schauen wir uns die Idee in einem kleinen Beispiel an

$fHandle = fopen("Datei.zip", "r");
while( !feof($fHandle) )
{
  echo fread($fHandle, 250);
  usleep(250000);
}
fclose($fHandle);

Zuerst öffnen wir einen Handler auf die Datei, die zum Download angeboten werden soll und bauen eine normale Schleife, die bis zum Dateiende regelmäßig Daten lesen wird und per echo weiterleitet. In unserem Fall lesen wir 250 Bytes. Danach lassen wir das PHP-Skript für 250ms schlafen. Theoretisch könnte man auch die Funktion sleep() nutzen und ganze Sekunden schlafen. Das vereinfacht die mathematischen Berechnungen für eine gewünschte, maximale Download-Geschwindigkeit, allerdings kann es passieren, dass bestimmte Verbindungen den eigentlichen Download abbrechen werden, aufgrund des langen Zeitraums der Inaktivität. Daher ist es sicherer mit der Funktion usleep() zu arbeiten, aber Achtung (!), diese arbeitet nicht mit Millisekunden, wovon viele fälschlicherweise ausgehen, sondern mit Mikrosekunden.
Folglich beinhaltet unser Skript auch einen Wert von 250.000 Mikrosekunden, d.h. 1/4 einer Sekunde. In der Gesamtbetrachtung übertragen wir mit diesem Skript rund 1 kB pro Sekunde. Für ein echtes Download-Skript natürlich bei weitem nicht ausreichend, aber für die Veranschaulichung sollte es seinen Zweck erfüllen.

Die entsprechende Geschwindigkeit lässt sich relativ leicht errechnen, wenn wir fix vier mal pro Sekunde an die Ausgabe weiterleitet. Die gewünschte Geschwindigkeit muss dazu schlicht in Bytes umgewandelt und durch 4 geteilt werden.
Zusammen mit anderen Faktoren lässt sich so sehr schnell ein individueller Downloadmanager für die eigene Webseite programmieren, der je nach Nutzerkreis entsprechende Vorteile genießen kann.