Nico R.: File-Upload mit Fortschrittsbalken

Hallo zusammen,

wie in diesem Beitrag besprochen, habe ich das Tutorial zum File Upload an meine Bedürfnisse angepasst (bei mir wird z.B. keine Dateiliste angezeigt, sondern nur die Vorschaubilder). Beim Upload wird jetzt ein Fortschrittsbalken angezeigt, und zwar (nach den Hinweisen von Felix und Rolf) nicht mehr über PHP ($_SESSION["upload_progress..."]), sondern per JS.

Dazu speichere ich zunächst die Dateigrößen aller ausgewählten Bilddateien im Array file_sizes[]. Nach dem Klick auf submit durchlaufe ich dieses Array und packe, je nachdem wie groß die Dateien sind und wie groß die maximale Paketgröße definiert ist, einzelne Versand-Pakete (bzw. Unterarrays mit den Schlüsseln der Dateien) im Array pakete[]. Danach schicke ich das erste Paket per fetch ab, warte auf den response und schicke dann das nächste Paket usw.

Entsprechend wird dann auch der Fortschrittsbalken gefüllt. Bei nur ein oder zwei Paketen ist das natürlich recht grob, bei größeren Dateimengen baut sich der Balken etwas "flüssiger" auf. Ist insgesamt vielleicht eine recht grobe Methode, aber dafür funktioniert es recht simpel und man sieht zumindest irgendeinen Fortschritt.

Das Script zum Testen findet ihr hier: https://fsv-optik.de/tests/galerie_upload.html Die maximale Paketgröße ist im Beispiel auf 5 MB eingestellt und man kann nur JPG-Dateien auswählen.

Vielleicht kann das Script ja als Vorlage zur Erweiterung des Tutorials dienen. Perfekt ist es mit Sicherheit nicht. An dem Deutsch-Englisch-Mischmasch der Variablennamen wird sich sicherlich der ein oder andere stören 😏 Hinweise oder Verbesserungsvorschläge sind natürlich willkommen.

Schöne Grüße

Nico

  1. Hallo Nico,

    wenn viele kleinere Dateien zu übertragen sind, ist das ein durchaus brauchbares Vorgehen.

    Bei wenigen großen Dateien ist es natürlich schwieriger. Was man tun kann, um den Kunden zu ermutigen, ist, nach der ersten Datei eine aktuelle Uploadgeschwindigkeit auszurechnen und daraus dann die verbleibende Restzeit zu bestimmen und anzuzeigen (die man dann im Sekundentakt herunterzählt). Das tut man nach jedem Upload erneut, und nach dem letzten sind es 0 Bytes und 0:00 Minuten.

    Manche Benutzer könnten irritiert davon sein, dass die Restzeit dann nach jeder Datei "springt", mal rauf, mal runter. Da muss man den Nutzen gegen den potenziellen Motz-Rückfluss abwägen.

    Rolf

    --
    sumpsi - posui - obstruxi
    1. Hallo Rolf,

      wenn viele kleinere Dateien zu übertragen sind, ist das ein durchaus brauchbares Vorgehen.

      Ja, für meinen Zweck ists in Ordnung. Da sinds in der Regel um die 50 Dateien mit je ca. 2 MB. Das heißt, da entstehen etwa 10 Pakete, da sieht das dann schon okay aus.

      Bei wenigen großen Dateien ist es natürlich schwieriger. Was man tun kann, um den Kunden zu ermutigen, ist, nach der ersten Datei eine aktuelle Uploadgeschwindigkeit auszurechnen und daraus dann die verbleibende Restzeit zu bestimmen und anzuzeigen (die man dann im Sekundentakt herunterzählt). Das tut man nach jedem Upload erneut, und nach dem letzten sind es 0 Bytes und 0:00 Minuten.

      Ahja, das ist natürlich auch keine schlechte Idee.

      Manche Benutzer könnten irritiert davon sein, dass die Restzeit dann nach jeder Datei "springt", mal rauf, mal runter. Da muss man den Nutzen gegen den potenziellen Motz-Rückfluss abwägen.

      In dem Fall könnte man den Wert ja einfach nicht aktualisieren, sondern normal weiter runterzählen, so lange bis es sich (so Gott will) wieder geglättet hat. Im schlechtesten Fall baut man natürlich immer mehr Rückstand auf und muss dann ggf. einen Riesensprung machen.

      Vielleicht ists besser, gar keine Restzeit anzuzeigen, sondern das auch in einen Wert für den progress-Balken umzurechnen.

      Gruß

      Nico

  2. Hallo zusammen,

    https://fsv-optik.de/tests/galerie_upload.html

    Vielleicht kann das Script ja als Vorlage zur Erweiterung des Tutorials dienen.

    Vielen Dank für deine Arbeit und dein Angebot!

    Grundproblem unserer Tutorials ist es oft, dass man etwas Neues vorstellen will und dann eine Lösung auf der Suche nach einem Problem anbietet. Oft sind diese dann nicht praktikabel.

    Hier haben wir ein gutes Beispiel als Grundlage für ein Tutorial!

    Perfekt ist es mit Sicherheit nicht. An dem Deutsch-Englisch-Mischmasch der Variablennamen wird sich sicherlich der ein oder andere stören 😏

    Das sind alles Kleinigkeiten! Nochmals danke!

    Hinweise oder Verbesserungsvorschläge sind natürlich willkommen.

    Herzliche Grüße

    Matthias Scharwies

    --
    Was ist eine Signatur?
  3. Hallo zusammen,

    ich habe gerade festgestellt, dass bei größeren Dateimengen nicht alle Dateien hochgeladen werden, sondern maximal 20. In der php.ini ist max_file_uploads mit 20 angegeben, also nehme ich mal gaaanz stark an, dass das zusammenhängt.

    Zumal ein Test mit mehreren 20 MB großen Dateien ergeben hat, dass auch hier nur eine hochgeladen wurde. Der Wert für post_max_size steht bei 25.

    Das heißt, das Problem der serverseitigen Upload-Limitierung durch PHP, das ich mit dem Packen der fetch-Pakete eigentlich lösen wollte, hab ich noch gar nicht gelöst. Offenbar handelt es sich für den Server trotzdem um einen einzigen Request.

    Ist das behebbar? Hab ich mich zu früh gefreut, und meine Lösung ist gar keine?

    Schöne Grüße

    Nico

    1. Hi,

      Das heißt, das Problem der serverseitigen Upload-Limitierung durch PHP, das ich mit dem Packen der fetch-Pakete eigentlich lösen wollte, hab ich noch gar nicht gelöst. Offenbar handelt es sich für den Server trotzdem um einen einzigen Request.

      Ist das behebbar? Hab ich mich zu früh gefreut, und meine Lösung ist gar keine?

      2 Varianten:

      1. Server-Config anpassen
      2. jede Datei einzeln abschicken

      cu,
      Andreas a/k/a MudGuard

    2. Hallo,

      ich habe gerade festgestellt, dass bei größeren Dateimengen nicht alle Dateien hochgeladen werden, sondern maximal 20. In der php.ini ist max_file_uploads mit 20 angegeben, also nehme ich mal gaaanz stark an, dass das zusammenhängt.

      das deutet stark darauf hin, dass du die Requests nicht sauber sequentiell absetzt, sondern zeitlich überlappend. Also den nächsten Request schon sendest, bevor die Response vom aktuellen komplett übertragen ist.

      Zumal ein Test mit mehreren 20 MB großen Dateien ergeben hat, dass auch hier nur eine hochgeladen wurde. Der Wert für post_max_size steht bei 25.

      Das ist nicht schlüssig. Auch bei mehreren parallelen Requests müsste jeder für sich zählen, weil jeder eine eigene Script-Instanz in PHP startet.

      Das heißt, das Problem der serverseitigen Upload-Limitierung durch PHP, das ich mit dem Packen der fetch-Pakete eigentlich lösen wollte, hab ich noch gar nicht gelöst. Offenbar handelt es sich für den Server trotzdem um einen einzigen Request.

      Nein, das nicht. Ich vermute, du sendest deine POST-Requests überlappend. Ich vermute die Ursache eher clientseitig. Du solltest vielleicht mal den Netzwerk-Traffic in den Developer-Tools deines bevorzugten Browsers kritisch beobachten. Und dann Schlüsse daraus ziehen.

      Einen schönen Tag noch
       Martin

      --
      Manchmal kann man gar nicht so viel fühlen, wie man denkt.
      Und manchmal fühlt man so viel, dass man gar nicht denken kann.
      1. Hallo Martin,

        das deutet stark darauf hin, dass du die Requests nicht sauber sequentiell absetzt, sondern zeitlich überlappend. Also den nächsten Request schon sendest, bevor die Response vom aktuellen komplett übertragen ist.

        Ja, das schien mir in einigen Testdurchläufen wirklich so zu sein. Deshalb hab ich im PHP-Upload-Script nach dem Durchlaufen der Schleife mit move_uploaded_file() am Ende ein echo "ok" angehangen, um sicherzugehen, dass das Script fertig ist. Auch das JS hatte ich jetzt noch einmal umgebaut und frage dort explizit nach dem "ok":

        (async function() { 
          let response = await fetch("sql/galerie_upload.php", {
            method: "POST",
            body: formData 
          });
        				
          if(response.ok) {
            const meldung = await response.text();
            if(meldung == "ok") {
              ...
              // fetch() neu starten
              paket_senden(paket_index+1);
            }						
          }
        })();
        

        Die jeweils nächste galerie_upload.php wird laut Netzwerkanalyse erst neu gestartet, wenn aus dem vorherigen Request das "ok" zurückkommt. Muss ich explizit noch etwas tun, um die Verbindung zum Server zu trennen oder ähnliches?

        In der Netzwerkanalyse wird hinter galerie_upload.php übrigens eine putzige Schildkröte angezeigt: "Langsame Serverantwortzeit (4,32 s). Das empfohlene Limit beträgt 500 ms."

        Schöne Grüße

        Nico