preload
Sep 11

Da das PECL-Package APC (Alternative PHP Cache) bisher bei mehreren parallelen Uploads unzuverlässige Fortschritts-Werte liefert (getestet mit Version 3.0), wurde bisher auf eine Perl-Lösung ausgewichen. Schöner wäre es allerdings, eine reine PHP-Lösung zu haben. Glücklicherweise gibt es noch ein weiteres PECL-Package namens uploadprogress, das mit Version 1.0 nun erstmals als stabile Version vorliegt. Die uploadprogress-Extension für php funktioniert auch bei mehreren parallelen Uploads und lässt sich recht einfach installieren…

Voraussetzungen

  • Mindestens PHP 5.2.0

Installation unter Windows

Für Windows werden direkt kompilierte Bibliotheken zum Download angeboten. Diese können von der pecl4win-Seite heruntergeladen werden: pecl4win.php.net. Diese Seite ist allerdings momentan (während dieser Artikel entstanden ist) nicht verfügbar. Eine alternative Download-Seite ist diese: downloads.php.net/pierre. Auf dieser Seite muss man lediglich nach php_uploadprogress suchen und die entsprechende Version herunterladen. Alternativ hier der direkte Link zu Version 1.0.0 Win32.

Nachdem das Zip-Archiv heruntergeladen und entpackt wurde, muss Datei php_uploadprogress.dll in das PHP-Extension-Verzeichnis kopiert werden. Anschließend müssen in Datei php.ini noch folgende Zeilen ergänzt werden:

extension=php_uploadprogress.dll

uploadprogress.file.filename_template="C:/Ihr-Temp-Ordner/upload_status_%s"
uploadprogress.file.contents_template="C:/Ihr-Temp-Ordner/upload_contents_%s"
uploadprogress.get_contents=0

Installation unter Linux

Unter Linux muss zunächst das aktuellste stabile Paket heruntergeladen werden: pecl.php.net/package/uploadprogress. Ist das Archiv entpackt, kann in das enthaltene Unterverzeichnis gewechselt werden. In diesem sind dann per Konsole folgende schritte auszuführen:

phpize
./configure
make
sudo make install

Während make install wird eine Meldung darüber ausgegeben, wohin die erstellte Datei uploadprogress.so verschoben wurde. Entspricht dieses Verzeichnis nicht dem eingestellten PHP-Extension-Verzeichnis, so muss die Datei ggf. von dem angegebenen Verzeichnis in das PHP-Extension-Verzeichnis verschoben werden. Anschließend müssen in Datei php.ini folgende Zeilen ergänzt werden:

extension=uploadprogress.so

uploadprogress.file.filename_template="/tmp/upload_status_%s"
uploadprogress.file.contents_template="/tmp/upload_contents_%s"
uploadprogress.get_contents=0

Anmerkung zu den Einstellungen in Datei php.ini

Über die uploadprogress-Extension kann auch auf die Inhalte hochgeladener Dateien zugegriffen werden. Dies empfielt sich jedoch nicht, da Uploads sonst sehr langsam werden. Daher sollte die Einstellung uploadprogress.get_contents deaktiviert werden, indem sie auf 0 (die Zahl Null) gesetzt wird (siehe oben). Es ist auf jeden Fall sicherzustellen, dass die Ordner, in denen später upload_status_%s und upload_contents_%s angelegt werden sollen, vorhanden und beschreibbar sind. Die Dateinamen selbst können angepasst werden, jedoch muss unbedingt %s enthalten sein, da dieser Teil beim Upload von der Extension durch einen eindeutigen Kenner ersetzt wird.

HTML-Formular und Javascript

Zunächst wird ein Formular benötigt, bei dem das target-Attribut auf einen iframe zeigt (target="iframe-id"). Im onsubmit-Event des Formulars wird die Javascript-Update-Funktion für den Fortschritt aufgerufen, am besten mit einer kleien Verzögerung. (Bitte beachten Sie, dass XHTML im Strict-Mode für Formulare kein target-Attribut kennt.)

Damit uploadprogress den Upload identifizieren kann, benötigt die Extension einen eindeutigen Kenner. Dieser wird per POST über ein Feld übergeben, das UPLOAD_IDENTIFIER heißen muss. Im Beispiel wird für diesen Kenner die Session-ID mit einer eindeutigen, von PHP generierten ID kombiniert. (Natürlich muss dazu zuvor ein Aufruf von session_start() stattgefunden haben.) Wichtig: Es ist unbedingt zu beachten, dass das Feld UPLOAD_IDENTIFIER vor den file-Feldern steht.

<form action="upload.php"
      method="post"
      enctype="multipart/form-data"
      target="upload_target"
      onsubmit="setTimeout('refreshUploadProgress();',500);">
  
  <input type="hidden"
         name="UPLOAD_IDENTIFIER"
         id="UPLOAD_IDENTIFIER"
         value="<?php echo uniqid(session_id() . "_"); ?>" />
  
  <input type="hidden" name="MAX_FILE_SIZE" value="10000000000000" />
  
  <p>
    Datei wählen:<br />
    <input type="file"
           maxlength="255"
           size="40"
           id="fUPLOAD_FILE"
           name="fUPLOAD_FILE" />
  </p>
  
  <p>
    <input type="submit" name="btnSave" value="Jetzt hochladen" />
  </p>
  
</form>

Die Javascript-Funktion refreshUploadProgress() ruft per AJAX-Request periodisch solange die Informationen über den Upload-Fortschritt ab und zeigt sie an, bis der Upload beendet wurde. Für das Absenden des Requests und das Empfangen und Auswerten der Antwort können Sie ihre bevorzugte Javascript-Bibliothek verwenden. Wichtig: Der Wert aus Feld UPLOAD_IDENTIFIER muss per Javascript mit an die PHP-Datei übergeben werden, die die Informationen über den Fortschritt des Uploads ausliest.

Upload-Fortschritt ermitteln

Der per Get oder Post übergebene eindeutige Kenner wird an Funktion uploadprogress_get_info(...) übergeben, die von der uploadprogress-Extension zur Verfügung gestellt wird:

uploadprogress_get_info($_GET["UPLOAD_IDENTIFIER"]);

Die Funktion liefert nun einige Informationen über den Verlauf des Uploads. Hier ein Beispiel:

array(11) {
  ["upload_id"]=>
  string(40) "gr9rim68iuvc3ubb2ao66jpkg1_4aa9f36776419"
  ["fieldname"]=>
  string(12) "fUPLOAD_FILE"
  ["filename"]=>
  string(19) "upload-test.txt"
  ["time_start"]=>
  string(10) "1252651882"
  ["time_last"]=>
  string(10) "1252651883"
  ["speed_average"]=>
  string(8) "35173072"
  ["speed_last"]=>
  string(8) "35167807"
  ["bytes_uploaded"]=>
  string(8) "35173072"
  ["bytes_total"]=>
  string(9) "168132200"
  ["files_uploaded"]=>
  string(1) "0"
  ["est_sec"]=>
  string(1) "3"
}

Diese Informationen können nun je nach Anforderung ausgewertet und (üblicherweise JSON-formatiert) ausgegeben werden. Die Javascript-Funktion, die letztendlich die Antwort vom Server erhält, kann bspw. eine textuelle und/oder grafische Anzeige realisieren.

Wenn der Upload fertig ist

Datei upload.php übernimmt die php-seitige Behandlung der hochgeladenen Dateien. Hier kann bspw. mit dem $_FILES-Array wie gewohnt vorgegangen werden. Wird der iFrame angezeigt, können die Informationen (evtl. auch Fehlermeldungen) bspw. direkt als HTML ausgegeben werden. Ist der iFrame versteckt, kann bspw. im onload-Event der Seite im iFrame eine Javascript-Funktion aufgerufen werden, die bspw. das innerHTML des Elementes mit den Meldungen an ein Element oder eine Javascript-Funktion von window.parent übergibt.

Damit die Uploads auch bei ausgeschaltetem Javascript funktionieren, empfielt es sich, das target-Attribut des Formulars erst im onsubmit-Event zu setzen. Ist Javascript deaktiviert, wird somit upload.php direkt geladen und die hochgeladenen Dateien werden trotzdem verarbeitet und es werden trotzdem die Meldungen ausgegeben.

Testumgebung

Getestet wurde mit folgenden Systemen:

  • Windows XP SP3 mit PHP 5.2.9 und uploadprogress 1.0.0
  • openSUSE 11.1 mit PHP 5.2.9 und uploadprogress 1.0.1

2 Responses to “Upload-Fortschritt mit 100% PHP und AJAX”

  1. site rencontre gratuit Says:

    My programmer is trying to convince me to move to .net from
    PHP. I have always disliked the idea because of the costs.
    But he’s tryiong none the less. I’ve been using Movable-type on various
    websites for about a year and am worried about switching to another platform.
    I have heard fantastic things about blogengine.net. Is there a way I can import all
    my wordpress posts into it? Any help would be greatly appreciated!

  2. Makler Heidelberg Says:

    Der Artikel ist wirklich gut. Das Thema hat mich schon sehr lange interessiert
    und ich konnte hier noch einiges weiterführendes finden. Ich freue mich,
    weitere Blogeinträge zu lesen. Danke und Grüße aus Heidelberg Marco Feindler

Leave a Reply

You must be logged in to post a comment.