Fortschrittsanzeige beim Hochladen von Dateien

Ein roter FortschrittsbalkenDas Hochladen von Dateien kann unter Umständen sehr lange, manchmal sogar Stunden dauern. Normalerweise gibt der Browser dem Benutzer aber keine Rückmeldung darüber, wie viele Daten schon hochgeladen wurden, bzw. wie lange der Vorgang noch dauern wird. Mit APC lassen sich während des Hochladevorgangs Informationen über selbigen ermitteln, und diese können benutzt werden um eine Fortschrittsanzeige für den Benutzer zu erstellen. Der Fortschritt könnte beispielsweise als Prozentwert, als Balken, oder als absoluter Wert (123/567 MiB) angezeigt werden. Eine Fortschrittsanzeige beim Hochladen steigert die Benutzerfreundlichkeit der Seite.

Wie man diese Funktionalität von APC benutzt, was vorausgesetzt wird, und welches Informationen APC liefern kann erkläre ich in diesem Artikel.

Voraussetzungen

Um den Hochladevorgang mittels APC zu überwachen, bzw. Informationen über ihn zu erhalten, werden einige Dinge vorausgesetzt. Natürlich muss APC installiert und aktiviert sein. In der APC-Konfiguration muss die Option apc.rfc1867 auf On stehen. Der Benutzer muss JavaScript in seinem Browser aktiviert haben, denn sonst kann der Fortschritt nicht mittels AJAX abgefragt werden. Außerdem muss das Hochlade-Formular um ein weiteres input-Element erweitert werden. Als Ziel-Adresse für das Formular wird ein iframe verwendet.

Die Werte folgender beider APC-Optionen müssen bekannt sein:

  • Der Präfix: apc.rfc1867_prefix (upload_)
  • Der Name des Formularelements: apc.rfc1867_name (APC_UPLOAD_PROGRESS)

(Standardwerte in Klammern, wenn nichts an der Konfiguration geändert wurde, kann man davon ausgehen das APC diese Werte verwendet. Der Einfachheit halber verwende ich in dieser Anleitung in Beispielen immer die Standardwerte, wenn man andere Werte benutzt, müssen diese natürlich entsprechend angepasst werden.)

Das Formular

Das Hochlade-Formular muss etwas erweitert werden. Zuerst wird ein zusätzliches input-Element mit dem Namen APC_UPLOAD_PROGRESS erstellt, welches einen eindeutigen Wert zugewiesen bekommt. Über diesen Wert kann später der Hochladevorgang verfolgt werden, deshalb ist es auch wichtig, dass der Wert einzigartig ist, sonst funktioniert das ganze nicht mehr, wenn mehrere Benutzer gleichzeitig Dateien hochladen. Mit der PHP-Funktion uniqid() ist das aber kein Problem, sie erstellt eindeutige IDs.

<input type="hidden" name="APC_UPLOAD_PROGRESS" value="<?php echo uniqid(); ?>" />

Wenn eine Datei hochgeladen wird, blockiert die Seite normalerweise, und der Benutzer kann nichts auf der Seite machen, bis die Datei vollständig hochgeladen wurde. Ist die Datei dann hochgeladen, bekommt der Benutzer die Ausgabe der Ziel-Seite (action) des Formulars zu sehen. Wenn man eine Fortschrittsanzeige erstellen möchte ist das natürlich ungünstig. Deshalb erstellt man ein iframe, und weist dem Formular dieses iframe als target zu. Dadurch wird die Ziel-Seite im iframe geladen, und die Formular-Seite blockiert nicht.

Das sollte man aber nur machen, wenn der Benutzer JavaScript aktiviert hat, und somit die Fortschrittsanzeige sehen kann. Deswegen wird die target-Eigenschaft dem Formular per JavaScript zugewiesen. Hat der Benutzer JavaScript deaktiviert, verhält sich das Formular wie jedes andere auch (ohne Fortschrittsanzeige, aber mit Blockieren), ist JavaScript aktiviert sieht der Benutzer die Fortschrittsanzeige und die Seite blockiert nicht.

<iframe src="upload.php" name="versteckt" style="display:none;"></iframe>
<form name="dateihochladen" action="upload.php" method="post" enctype="multipart/form-data">
<script type="text/javascript">document.forms.dateihochladen.target = 'versteckt';</script>

Fehlt nur noch der AJAX-Teil des ganzen. Vorher benötigt man allerdings noch eine PHP-Datei, die die Informationen über den Vorgang liefert.

<?php
var_dump(apc_fetch('upload_' . $_GET['id']));
?>

Mittels apc_fetch werden die Informationen aus dem Zwischenspeicher ausgelesen, hierzu wird die eindeutige ID benötigt, welche man praktischerweise einfach über die URL überträgt und mittels $_GET abfragt. Anstatt das komplette Feld mit allen Informationen per var_dump auszugeben, kann man es natürlich auch per json_encode umwandeln (und dann gleich im JavaScript weiterverwenden), oder per PHP gleich die Fortschrittsanzeige in HTML ausgeben. Aber das ist jedem selbst überlassen.

Der AJAX-Teil ist auch nicht weiter schwer, man ruft einfach nach dem Absenden des Formulars in bestimmten Zeitintervallen die obige PHP-Datei ab, und verarbeitet die Informationen weiter, bzw. gibt sie aus. Komplett sieht das ganze dann so aus:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
                      "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" dir="ltr" lang="de-DE">
  <head>
    <title>Fortschrittsanzeige beim Hochladen von Dateien</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <script type="text/javascript">
    function aktualisiere() {
      var anfrage = null;
 
      if (window.XMLHttpRequest) {
        anfrage = new XMLHttpRequest();
      }
      else if (window.ActiveXObject) {
        anfrage = new ActiveXObject("Microsoft.XMLHTTP");
      }
 
      anfrage.onreadystatechange = function() {
       if ((anfrage.readyState == 4) && (anfrage.status == 200)) {
          document.getElementById('status').innerHTML = anfrage.responseText;
        }
      }
 
      anfrage.open('GET', 'status.php?id=' + 
                    document.forms.dateihochladen.APC_UPLOAD_PROGRESS.value);
      anfrage.send(null);
    }
 
    function ladeHoch(elem) {
      elem.disabled = true;
 
      setInterval('aktualisiere()', 1000);
    }
    </script>
    <link rel="stylesheet" type="text/css" href="stil.css" />
  </head>
  <body>
    <h1>Dateien hochladen mit Fortschrittsanzeige</h1>
    <div id="inhalt">
      <iframe name="versteckt" style="display:none;"></iframe>
      <form name="dateihochladen" action="upload.php" 
            method="post" enctype="multipart/form-data">
      <script type="text/javascript">
        document.forms.dateihochladen.target = 'versteckt';
      </script>
        <fieldset>
          <legend>Datei</legend>
          <input type="hidden" name="APC_UPLOAD_PROGRESS" 
                 value="<?php echo uniqid(); ?>" />
          <input type="file" name="datei" />
          <input type="submit" value="Hochladen" 
                 onclick="ladeHoch(this);" />
        </fieldset>
        <div id="status"></div>
      </form>
    </div>
  </body>
</html>

In der Form funktioniert das Skript bereits, es macht aber nichts anderes, als das von APC zurückgegebene Feld mit Informationen im status-div auszugeben.

Welche Informationen bekommt man?

Welche Informationen APC zurückgibt hängt davon ab, ob die Datei gerade hochgeladen wird, oder der Hochladevorgang bereits abgeschlossen ist.

Wird die Datei gerade hochgeladen, enthält das von APC zurückgegebene Feld folgende Informationen:

Element Inhalt Typ
total Größe der Datei in Bytes int
current Bereits hochgeladene Bytes int
filename Name der Datei string
name Name des Formularelement (Datei-Element) string
done Hochladen abgeschlossen? int (0/1)
start_time Wann wurde der Hochladevorgang begonnen (Unix-Zeitstempel) float

Wenn der Hochladevorgang abgeschlossen wurde, kommen noch folgende Elemente hinzu:

Element Inhalt Typ
rate Wie viele Bytes pro Sekunde übertragen wurden float
temp_filename Der temporäre Dateiname string
cancel_upload 0 Wenn die Datei erfolgreich hochgeladen wurde, sonst >0 (Fehlercode) int

Mehrere Dateien hochladen?

Es ist auch möglich den Hochladevorgang von mehreren Dateien zu überwachen, man kann aber erst nach dem Hochladen feststellen, wie groß jede einzelne Datei war/ist, da APC die Dateigrößen zusammenrechnet. Braucht man diese Information unbedingt, kann man mehrere Formulare mit je einer Datei, und unterschiedlichen APC_UPLOAD_PROGRESS-IDs erstellen.

Wie erstelle ich denn jetzt einen Fortschrittsbalken?

Wie man Fortschrittsbalken erstellt, habe ich in diesem Artikel beschrieben. Man könnte obiges Skript wie folgt modifizieren um einen Fortschrittsbalken zu implementieren (ich verwende in diesem Beispiel die dwProgressBar):
status.php

<?php
echo json_encode(apc_fetch('upload_' . $_GET['id']));
?>

hochladen.php

<!-- [...] -->
		<script src="mootools-1.2.3-core-yc.js" type="text/javascript"></script>
		<script src="dwprogressbar.js" type="text/javascript"></script>
		<script type="text/javascript">
			function aktualisiere() {
				var anfrage = null;
 
				if (window.XMLHttpRequest) {
					anfrage = new XMLHttpRequest();
				}
				else if (window.ActiveXObject) {
					anfrage = new ActiveXObject("Microsoft.XMLHTTP");
				}
 
				anfrage.onreadystatechange = function() {
					if ((anfrage.readyState == 4) && (anfrage.status == 200)) {
						var ergebnis = eval('(' + anfrage.responseText + ')');
						if (ergebnis.done == 1) {
							if (ergebnis.cancel_upload == 0) {
								document.getElementById('status').innerHTML = 'Hochgeladen!';
							}
							else {
								document.getElementById('status').innerHTML = 'Fehler/Abgebrochen.';
							}
						}
						else {
							//document.getElementById('status').innerHTML = Math.round((ergebnis.current * 100) / ergebnis.total) + '%';
							document.fsb.set(Math.round((ergebnis.current * 100) / ergebnis.total));
						}
					}
				}
 
				anfrage.open('GET', 'status.php?id=' + document.forms.dateihochladen.APC_UPLOAD_PROGRESS.value);
				anfrage.send(null);
			}
 
			function ladeHoch(elem) {
				elem.disabled = true;
 
				document.fsb = new dwProgressBar({
					container: $('status'),
					startPercentage: 0,
					speed: 1000,
					boxID: 'rahmen',
					percentageID: 'balken',
					step: 10,
					allowMore:0,
					displayID: 'text',
					displayText: true
				});
 
				setInterval('aktualisiere()', 1000);
			}
		</script>
		<style type="text/css">
			#rahmen {
				background:url('gfx/normal.png') right center no-repeat; width:300px; height:30px;
				display:inline-block;
			}
			#balken {
				background:url('gfx/farbig.png') left center no-repeat; height:30px;
			}
			#text {
				font-size:16px;
				line-height:20px;
				vertical-align:top;
				padding-left:10px;
				display:inline-block;
			}
		</style>
<!-- [...] -->

Ich hoffe ich konnte euch einen kleinen Einblick in das Thema Fortschrittsanzeige beim Hochladen von Dateien geben. Anhand dieses Artikels (und der vorangehenden zum Thema APC/Dateien hochladen) sollte es möglich sein, ein individuelles Formular zum Hochladen von Dateien, mit Fortschrittsbalken, etc. zu erstellen.

Informationen

1 Stern2 Sterne3 Sterne4 Sterne5 Sterne (5 Bewertungen, Durchschnitt: 4,60 von 5)
Loading ... Loading ...
Kategorie: Java Script, PHP
Ansichten: 6.092

Kommentare

Bisher wurden 2 Kommentare geschrieben.

Kommentar schreiben

XHTML: Folgende Elemente sind erlaubt: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="">

Angetrieben durch Wordpress Thema erstellt von Antu