Klaus: Upload großer Dateien ohne Fehlermeldung?

Hallo,

über ein Upload-Script soll der Benutzer in der Lage sein, auch große Dateien auf den Server hochzuladen.
Bei kleineren Dateien (kleiner als 1GB) funktioniert noch alles, aber bei Dateien mit bspw. 1,3GB gibt die Funktion move_uploaded_file FALSE zurück, aber keine Fehlermeldung.
Weder in der error.log noch im Eventlog des Servers ist etwas zu sehen.

Das Formular hierzu ist denkbar einfach:

  
			<form name="upload" enctype="multipart/form-data" method="post">  
				<input type="hidden" name="<? echo ini_get("session.upload_progress.name"); ?>" value="upload" />  
				<input type="hidden" name="MAX_FILE_SIZE" value="5120000" />  
				<label for="fileupload">Datei, die verschickt werden soll:</label>  
				<input type="file" id="fileupload" name="fileupload" size="30" required="required" multiple  onchange="fileChange();" />  
				<br/>  
				<input class="button" type="button" value="Datei hochladen" onClick="fileUpload();" />  
			</form>  
  

Das dazugehörige Javascript ruft im Grunde nur das PHP-Script auf: (den Schnickschnack mit dem Upload-Balken hab ich hier mal weggelassen.)

  
function fileUpload() {  
	var file = document.getElementById("fileupload").files[0];  
	if (!file) {  
		return;  
	}  
	var formdaten = new FormData(document.upload);  
	var fileList = document.getElementById("fileupload").files;  
	for(var i =0; i < fileList.length; i++) {  
		var file = fileList[i];  
		formdaten.append("datei"+i, file);  
	}  
	req.onload = function(e) {  
		result = req.responseText;  
	        document.getElementById("fortschritt_txt").innerHTML = result;  
	}  
	url = 'upload.php';  
	req.open('post', url, true);  
	req.send(formdaten);  
}  

Das PHP-Script prüft, ob das Verzeichnis und die Berechtigung noch vorhanden ist.

  
	$anz_uploads = sizeof($_FILES) -1;  
  
	if (file_exists("/upload/files/") && is_writable("/upload/files/")) {  
		for ($i = 0; $i < $anz_uploads; $i++) {  
			$xdatei = "datei".$i;  
			$name = basename($_FILES[$xdatei]['name']);  
			$dateiname = "/upload/files/".$name;  
			$tmpname = $_FILES[$xdatei]['tmp_name'];  
			$erfolg = move_uploaded_file($tmpname, $dateiname);  
			if (!$erfolg) {  
				echo "Warning! Datei wurde nicht korrekt hochgeladen!";  
			}  
		}  

Früher waren noch ein paar PHP.ini Einträge zu niedrig gesetzt und da gabs wenigstens auch eine Warnung zurück.

Hier die (ich denke) relevanten Einträge:
file_uploads=On
upload_tmp_dir="C:\xampp\tmp"
upload_max_filesize=5G
max_file_uploads=50
post_max_size=0
max_execution_time=1200
max_input_time=60
memory_limit=-1
error_reporting=E_ALL & ~E_NOTICE

Gibt es sonst noch wo ein Rädchen zu drehen, damit auch wirklich große Uploads funktionieren?

Klaus

  1. Hakuna matata!

    Du lädst alle Dateien doppelt hoch:

    var formdaten = new FormData(document.upload);
    var fileList = document.getElementById("fileupload").files;
    for(var i =0; i < fileList.length; i++) {
    var file = fileList[i];
    formdaten.append("datei"+i, file);
    }

      
    Durch den Konstruktor FormData() werden schon Einträge für alle Dateien erzeugt. Durch die append()-Methode erzeugst du für jede Datei einen weiteren Eintrag.  
      
    
    > Das PHP-Script prüft, ob das Verzeichnis und die Berechtigung noch vorhanden ist.  
    >   
    > ~~~php
      
    
    > 	$anz_uploads = sizeof($_FILES) -1;  
    >   
    > 	if (file_exists("/upload/files/") && is_writable("/upload/files/")) {  
    > 		for ($i = 0; $i < $anz_uploads; $i++) {  
    > 			$xdatei = "datei".$i;  
    > 			$name = basename($_FILES[$xdatei]['name']);  
    > 			$dateiname = "/upload/files/".$name;  
    > 			$tmpname = $_FILES[$xdatei]['tmp_name'];  
    > 			$erfolg = move_uploaded_file($tmpname, $dateiname);  
    > 			if (!$erfolg) {  
    > 				echo "Warning! Datei wurde nicht korrekt hochgeladen!";  
    > 			}  
    > 		}  
    > 
    
    

    Damit ist auch klar, warum dieser PHP-Code nicht funktioniert. Nur die Hälfte der hochgeladenen Dateien hat Namen die der Form "datei".$i entsprechen. Nämlich nur solche, die du mit der append()-Methode so angehangen hast. Die erste Hälfte der Dateien hat Namen, die dem Formular-Element entsprechen, also "fileupload".

    --
    “All right, then, I'll go to hell.” – Huck Finn
    1. Damit ist auch klar, warum dieser PHP-Code nicht funktioniert. Nur die Hälfte der hochgeladenen Dateien hat Namen die der Form "datei".$i entsprechen. Nämlich nur solche, die du mit der append()-Methode so angehangen hast. Die erste Hälfte der Dateien hat Namen, die dem Formular-Element entsprechen, also "fileupload".

      Und dabei hatten wir erst vor kurzem besprochen, dass es besser ist die Multi-Felder mit etwas wie "fileupload[]" zu benennen.

      Jörg Reinholz

    2. Guten Morgen!

      Du lädst alle Dateien doppelt hoch:

      Ok, das stimmt(e). Das war noch ein Rest von vorangegangenen Problemen die Daten per Post zu übermitteln.
      Aber auch wenn ich jetzt nicht mehr doppelt hochlade

        
      	var formdaten = new FormData();  
      	var fileList = document.getElementById("fileupload").files;  
      	for(var i =0; i < fileList.length; i++) {  
      		var file = fileList[i];  
      	    formdaten.append("datei"+i, file);  
      	}  
      
      

      Das PHP-Script prüft, ob das Verzeichnis und die Berechtigung noch vorhanden ist.

      $anz_uploads = sizeof($_FILES) -1;  
      
      if (file_exists("/upload/files/") && is_writable("/upload/files/")) {  
        for ($i = 0; $i < $anz_uploads; $i++) {  
        	$xdatei = "datei".$i;  
        	$name = basename($_FILES[$xdatei]['name']);  
        	$dateiname = "/upload/files/".$name;  
        	$tmpname = $_FILES[$xdatei]['tmp_name'];  
        	$erfolg = move_uploaded_file($tmpname, $dateiname);  
        	if (!$erfolg) {  
        		echo "Warning! Datei wurde nicht korrekt hochgeladen!";  
        	}  
        }  
      
      
      >   
      > Damit ist auch klar, warum dieser PHP-Code nicht funktioniert. Nur die Hälfte der hochgeladenen Dateien hat Namen die der Form `"datei".$i`{:.language-php} entsprechen. Nämlich nur solche, die du mit der append()-Methode so angehangen hast. Die erste Hälfte der Dateien hat Namen, die dem Formular-Element entsprechen, also "fileupload".  
        
      Leider ist damit nicht klar, warum der Code nur bei großen Dateien nicht funktioniert.  
      Ok, ich hatte bei bspw. einer ausgewählten großen Datei, die Datei quasi doppelt hochgeladen,  
      also einmal in "fileupload" und nochmal in "datei", aber es hätte mir ja auch gereicht, wenn er die Datei wenigstens einmal hochgeladen hätte und es erklärt nicht, warum es nur bei großen Dateien größer 1GB nicht funktioniert.  
        
      Klaus  
        
        
        
        
      
      
      1. und es erklärt nicht, warum es nur bei großen Dateien größer 1GB nicht funktioniert.

        Möglicherweise killt der Apache die Verbindung wenn eine bestimmte Zeit lang keine Daten vom Server an den Client gesendet wurden.

        error_reporting=E_ALL & ~E_NOTICE - Hm. Du suchst nach Problemen. Wie wäre es mit:

        error_reporting(E_ALL);

        Ohnehin wirst Du beim Senden derartig großer Dateien auf eine Menge von Problemen stoßen: Browser -> ggf. Proxys -> (instabiles:NETZWERK) -> Webserver -> PHP

        Man kann also eine große Datei auch zerlegen:

        https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice

        Dazu würde ich dann raten. Wie man die einzelnen Blob-Parts dann sendet findest Du im Web. Gib den Dingern intelligente Namen und, das wäre mein Tipp: versuche nicht erst, die in PHP zusammenzufügen, mach das mit cat filename.part.00000 ... filename.part.n > filename (und achte auch eine saubere Sortierung).

        Ich denke, ein derart spezieller Anwendungsfall rechtfertigt es, dass man auf die Implementierung reinsten PHPs und damit die Systemunabhängigkeit verzichtet.

        Jörg Reinholz

        1. Möglicherweise killt der Apache die Verbindung wenn eine bestimmte Zeit lang keine Daten vom Server an den Client gesendet wurden.

          Das werde ich mal prüfen, ob das seitens Apache etwas eingestellt werden kann/muss.

          error_reporting=E_ALL & ~E_NOTICE - Hm. Du suchst nach Problemen. Wie wäre es mit:

          error_reporting(E_ALL);

          Naja, Probleme sind idR keine Meldungen vom Typ Notiz. Daher war es für mich nicht verwunderlich, dass auch damit keine Meldung ausgegeben wird.

          Ohnehin wirst Du beim Senden derartig großer Dateien auf eine Menge von Problemen stoßen: Browser -> ggf. Proxys -> (instabiles:NETZWERK) -> Webserver -> PHP

          Großartige Probleme erwarte ich nicht, da der Upload im internen Netzwerk passiert, also mind. stabile 100Mbit.

          Man kann also eine große Datei auch zerlegen:

          https://developer.mozilla.org/en-US/docs/Web/API/Blob/slice

          Dazu würde ich dann raten. Wie man die einzelnen Blob-Parts dann sendet findest Du im Web. Gib den Dingern intelligente Namen und, das wäre mein Tipp: versuche nicht erst, die in PHP zusammenzufügen, mach das mit cat filename.part.00000 ... filename.part.n > filename (und achte auch eine saubere Sortierung).

          Wenn alle Stricke reissen, dann werde ich die Dinger wohl zerlegen müssen.

          Mir ist halt schleierhaft, warum ich einfach nirgends eine Fehlermeldung finden kann.

          Klaus

          1. Mir ist halt schleierhaft, warum ich einfach nirgends eine Fehlermeldung finden kann.

            Naja. Das ist jetzt so eine Sache... Möglicherweise kommt das, was sich von Ergebnis her als Fehler auswirkt dem Interpreter wie etwas vor, was ihm nur eine Notiz wert ist. (PHP halt) Prüfst Du denn den http-statuscode der Antwort?

            use strict mit PHP

            Jörg Reinholz

            1. Naja. Das ist jetzt so eine Sache... Möglicherweise kommt das, was sich von Ergebnis her als Fehler auswirkt dem Interpreter wie etwas vor, was ihm nur eine Notiz wert ist. (PHP halt) Prüfst Du denn den http-statuscode der Antwort?

              use strict mit PHP

              Ich habe das use_strict.php eingebaut (auch wenn ich denke, dass ich alle Einstellungen schon in der PHP.ini gesetzt hatte), eine Fehlermeldung / Warnung / Info gab es auch nicht.

              error_reporting=E_ALL
              display_errors=On
              display_startup_errors=On
              log_errors=On
              log_errors_max_len=1024
              ignore_repeated_errors=Off
              ignore_repeated_source=Off
              report_memleaks=On
              track_errors=On
              html_errors=On

              error.log, access.log und php-error.log bleiben ohne den geringsten Hinweis.

              Zwischenzeitlich habe ich auch noch ausgeschlossen, dass es vielleicht an den Dateitypen liegen oder an langen untypischen Namen (mit mehreren Punkten) könnte.
              ZIP-Files < 1GB lassen sich hochladen, über 1GB nicht.

              Klaus

              1. Lieber Klaus,

                ZIP-Files < 1GB lassen sich hochladen, über 1GB nicht.

                ♫ *singselz* ♪♫ Plupload ♫ *dumdidum* ♪♫ ;-P

                Liebe Grüße,

                Felix Riesterer.

                --
                "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
  2. Lieber Klaus,

    für solche Fälle liebe ich Plupload.

    Liebe Grüße,

    Felix Riesterer.

    --
    "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
    1. Guten Morgen Felix,

      danke für den Link-Tip, aber ich sträube mich immer etwas gegen fremde Plugins.
      Ohne das jetzt vertiefen zu wollen, weiß ich gerne was genau wann wo passiert und muss mich nicht in fremde Strukturen einarbeiten, um dann doch das eine oder andere Extra einzubauen, was doch noch fehlt oder rauszunehmen, was zuviel ist etc.

      Klaus

      1. Lieber Klaus,

        danke für den Link-Tip, aber ich sträube mich immer etwas gegen fremde Plugins.

        Plupload ist kein Plugin. Es ist ein JavaScript. Du bindest es in Dein Dokument ein und es bietet Dir im Gegenzug einen Dateitransfer zum Server an, den Du PHP-seitig entgegen nimmst.

        Ohne das jetzt vertiefen zu wollen, weiß ich gerne was genau wann wo passiert und muss mich nicht in fremde Strukturen einarbeiten, um dann doch das eine oder andere Extra einzubauen, was doch noch fehlt oder rauszunehmen, was zuviel ist etc.

        Das ist das Schöne an Plupload: Du konfigurierst es so, wie Du es benötigst.

        Seit ich das Tool einsetze, sind mir serverseitige Beschränkungen nicht mehr begegnet, denn Plupload lädt eine Datei in Teilen hoch. Du brauchst auf dem Server lediglich die Teile wieder zusammenzusetzen. Und das klappt sehr schön zuverlässig!

        Liebe Grüße,

        Felix Riesterer.

        --
        "Wäre die EU ein Staat, der die Aufnahme in die EU beantragen würde, müsste der Antrag zurückgewiesen werden - aus Mangel an demokratischer Substanz." (Martin Schulz, Präsident des EU-Parlamentes)
        1. Plupload ist kein Plugin. Es ist ein JavaScript. Du bindest es in Dein Dokument ein und es bietet Dir im Gegenzug einen Dateitransfer zum Server an, den Du PHP-seitig entgegen nimmst.

          Ok, ich habe mich ungünstig ausgedrückt.

          Das ist das Schöne an Plupload: Du konfigurierst es so, wie Du es benötigst.

          Wenn ich das Plupload (wofür könnte wohl das "pl" stehen...) einsetzen wollte, müsste ich mich damit erstmal zeitintensiv auseinander setzen, wie das PHP-Script aussehen muss, damit es gegebenfalls

          • die Dateien gesplittet überträgt
          • bei der Auswahl mehrerer Dateien, diese automatisch zipped.
          • die hochgeladenen Dateien in einer DB speichert
          • einen kryptischen Download-Link erzeugt
            etc.
            Zudem müsste ich schaeun, wo ich das Script anpassen, damit nicht nur eine Prozent-Anzeige sondern auch ein Progressbar gefüllt wird.

          Nicht falsch verstehen. Das Plugin sieht gut aus und wenn ich direkt zu Beginn von dem Plupload  gewusst hätte, hätte ich dieses wohl auch genutzt und angepasst.

          Aber jetzt steht fast alles komplett. Es funktioniert alles wie erwartet, solange die Datei nicht größer 1GB wird.

          Klaus

  3. Tach!

    Gibt es sonst noch wo ein Rädchen zu drehen, damit auch wirklich große Uploads funktionieren?

    Ja, je nach Webserver-Konfiguration sträubt sich der bereits bei zu großen Requests (siehe LimitRequestBody). Und auch die FCGI-Programme haben eine Begrenzung. Üblicherweise läuft dann aber PHP gleich gar nicht los, wenn der Request schon daran scheitert.

    dedlfix.

    1. Guten Morgen!

      Tach!

      Gibt es sonst noch wo ein Rädchen zu drehen, damit auch wirklich große Uploads funktionieren?

      Ja, je nach Webserver-Konfiguration sträubt sich der bereits bei zu großen Requests (siehe LimitRequestBody). Und auch die FCGI-Programme haben eine Begrenzung. Üblicherweise läuft dann aber PHP gleich gar nicht los, wenn der Request schon daran scheitert.

      LimitRequestBody ist nicht gesetzt. Da die Voreinstellung 0 ist, sollte es hier kein Problem geben.
      FCGI-Programme nutze ich nicht. Nur HTML, Javascript und PHP-Code.

      1. Tach!

        FCGI-Programme nutze ich nicht. Nur HTML, Javascript und PHP-Code.

        Du benutzt PHP, das auf irgendeine Weise in den Apachen eingebunden ist. Wenn man es als Modul einbindet, ist das zwar die einfachste Art und Weise, aber man kann dann auch keine Anwendungen gegeneinander abschotten, weil alles als Apache-User läuft. Wenn es mehr auf Sicherheit ankommt, vor allem beim Shared-Hosting, ist es günstiger, PHP per FCGI in den Apachen einzubinden. Dann kann man auch mit suPHP und ähnlichen Hilfsmitteln separate Nutzer zuweisen und über Dateiberechtigungen Abschottungen vornehmen. Auf FCGI-Einbindungen von PHP zu treffen ist also keine Seltenheit sondern sollte der überwiegende Normalfall sein. (Die phpinfo()-Ausgabe zeigt im oberen Teil an, wie PHP in den Webserver eingebunden ist.)

        dedlfix.