Klaus1: PHP-Zippen von großen Dateien schlägt fehl

Hallo,

über ein Formular kann ein Anwender beliebig viele Dateien hochladen. Zusätzlich kann er entscheiden, ob die Dateien nach dem Upload in einer einzelnen Zip-Datei zusammengefasst werden sollen.

Das Zippen funktioniert bei kleinen Dateien problemlos, aber bei größeren Dateien erhalte ich Fehlermeldungen.

Ich hatte es zu Beginn mit dem Standard-PHP ZipArchive versucht, bin dann aber aus Performance-Gründen zum OS-Kommando gewechselt.

Der Code sieht im Moment wie folgt aus:

		$target_path = "/srv/www/htdocs/test/upload/".$folder;
		$zip_name = $target_path."/".$folder;
		$zip_target = $target_path."/*";
		$fp = popen("zip $zip_name -0 -j -q $zip_target", "r");
		fpassthru($fp);

		// Dateien im Folder können jetzt gelöscht werden
		foreach (new DirectoryIterator($target_path) as $fileInfo) {
			if ((!$fileInfo->isDot()) AND ($fileInfo->getFilename() != $folder.".zip")) {
				unlink($fileInfo->getPathname());
			}
		}

Die Fehlermeldung lautet wie folgt:

zip I/O error: No such file or directory zip error: Could not create output file (was replacing the original zip file)

Kann es sein, dass die Dateien bereits gelöscht werden, während der Zip-Prozess noch läuft? Kann ich das irgendwie sicherstellen, dass erst gelöscht wird, wenn der Prozess durch ist?

Kann ich eventuell schon beim Upload (ich lade die Dateien "schnipselweise" hoch) die Datei-Schnipsel (Chunks) in die Zip-Datei packen?

LG

Klaus

  1. Hallo Klaus1,

    popen erzeugt laut Handbuch einen Fork, d.h. einen neuen, parallel laufenden Prozess. Angeblich passiert das nicht unter Windows, da wartet PHP bis das Programm durch ist.

    Auf Unix-Systemen sollte der Ablauf parallel sein, und fpassthru sollte warten, bis zip durch ist. DAS solltest Du für Dich beweisen, indem Du ein paar ECHOs einbaust und zip mit -v und nicht mit -q laufen lässt. Das -v sollte Dir auch helfen, die Fehlermeldungen von ZIP besser verstehen zu können.

    Du solltest auch ein 2>&1 an die ZIP Befehlszeile hängen, um mögliche Ausgaben auf stderr mitzubekommen. popen liefert Dir nur stdout.

    Du solltest weiterhin überprüfen, ob zwei Anwender im gleichen $folder unterwegs sind. Zu diesem Zweck solltest Du Informationen ins Serverlog schreiben (welches Du hoffentlich lesen kannst). Wenn nicht, schreib eine eigene Log-Datei. Dieser create-Error deutet auf Parallelzugriff hin.

    Rolf

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

      ich habe das Script ein klein wenig angepasst und das Zip-File nicht im gleichen Ordner erstellt und erst nach dem Löschen in das Verzeichnis verschoben. Zudem habe ich den Schalter -v anstatt von -q gesetzt.

      Ich habe dann eine einzige Datei hochgeladen (ca. 500MB)

      Folgendes Resultat:

      Zum Start vom Zip wird die Fehlermeldung ausgegeben, dass sich die Größe der zu packenden Datei verändert hat. Und am Ende enthält die Zip-Datei neben der eigentlichen 500MB-Datei auch noch die Zip-Datei selber.

      D.h. das PHP-Script läuft definitiv, parallel zum Zip-Prozess, weiter.

      Dass zwei Anwender im gleichen $folder unterwegs sind, ist ziemlich unwahrscheinlich. Die müssten schon exakt in der selben Millisekunde gestartet haben. $folder wird aus dem Zeitstempel erstellt.

      LG Klaus

  2. Hm... Versuch mal, den gescheiterten Befehl auszugeben

    # … wie vor.
    $sys = 'zip' 
           . ' ' . escapeshellarg( $zip_name )
           . ' -0 -j -v'
           . ' ' . escapeshellarg( $zip_target )
    ;
    
    # /* Debug:
    echo '<pre>' . $sys . '</pre>';
    exit;
    # */
    
    
    $fp = popen( $sys, 'r' );
    # … wie vor.
    

    Wenn die Ausgabe Deinen Erwartungen entspricht, dann schalte das Debuggen wieder ab. Ändere dazu # /* Debug: zu /* Debug:

    1. Hallo Raketenwilli,

      die Ausgabe entspricht meinen Erwartungen, ändert aber nichts am Verhalten. Bei kleinen Dateien funktioniert es auch, da ist zip offenbar schnell genug, die Dateien zu packen, bevor die Dateien wieder gelöscht werden und die Zip-Datei ins Verzeichnis geschoben wird.

      Ich glaube ideal wäre es, wenn die Zip-Datei schon beim Upload erstellt wird und die einzelnen Chunks hinzufügt, mit der ich die Dateien hochlade. Ich habe nur bisher noch nichts gefunden, wie ich die Chunks innerhalb der Zip-Datei wieder zur jeweiligen Datei hinzufüge.

      Ich hab ja auch noch das Problem, dass der Anwender beim Zippen keine Rückinfo erhält, wie beim Hochladen (über einen Progress-Bar).

      LG Klaus

      1. Hallo Klaus1,

        Ich hab ja auch noch das Problem, dass der Anwender beim Zippen keine Rückinfo erhält, wie beim Hochladen (über einen Progress-Bar).

        Nee, das wird auch nicht gehen.

        Wie funktioniert denn deine Chunk-Übertragung? Normalerweise verwendet man für einen File Upload ein Formular mit einem <input type="file"> Element darin, und dann kümmert sich der Brauser darum. Bei Riesendateien rennst Du dann natürlich gegen PHP Limits an. Die kann man konfigurieren - man muss es nur dürfen. Knöpfe und Schalter

        Einen Upload-Progress bei großen Dateien kann man übrigens durch PHP erstellen lassen - ich selbst habe das noch nicht gemacht, aber hier steht, wie es gehen soll.

        Eine händische Kontrolle über den Ablauf würde bedeuten, dass Du mit dem File API die Datei erstmal im Browser in den Speicher saugst und dann XMLHttpRequests machst, um die Chunks hochzuladen, die Du am Server dann wieder zusammenbaust.

        Oder verwendest Du gar keinen Browser als Client?

        Rolf

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

          der Anwender kann per Drag&Drop oder über den Datei-Dialog aus beliebigen Verzeichnissen Dateien wählen. Beim Upload läuft Javascript dann durch alle gewählten Dateien, zerlegt die jeweilige Datei in Häppchen (Chunks) und lädt diese hoch. Im PHP werden die Chunks dann wieder zusammengesetzt. Damit lassen sich beliebig viele (ansonsten gibts eine Grenze bei der POST-Größe) und beliebig große Dateien (ansonsten PHP Max-Size, maximum-execution-time, etc.) hochladen.

          Der Anwender soll nun beim Hochladen noch wählen können, dass die Dateien gezippt werden sollen. Ebenso der potentielle Downloader, der bei mehreren Dateien wählen kann, ob alle Dateien zusammen (als Zip) heruntergeladen werden können. Letzteres kann ich eventuell als Stream realisieren. Ersteres aber wohl nicht.

          LG Klaus

      2. Hm.

        die Ausgabe entspricht meinen Erwartungen

        Die könnten falsch sein. Zeig mal her.

        $fp = popen( $sys, 'r' );
        fpassthru($fp);
        

        Bau trotzdem die Zeile mit dem explizitem $sys (das ist wegen des Debuggings/Wegloggens einfach besser). Und dann nimm passthru(), system() oder exec();

        if ( ! passthru( $sys, $resultCode ) ) {
           echo "Da lief was falsch: '$sys'\nResult-Code ist $resultCode";
           exit;
        }
        
        1. Hallo Raketenwilli,

          kann das Problem bei passthru und fpassthru sein, dass PHP gerade bei einem länger laufenden externen Programm denkt, dass keine Ausgabe mehr käme und annimmt, dass das Programm zu Ende sei?

          Andernfalls hätte ich erwartet, dass /f?passthru/ wartet, bis der angekoppelte Prozess zu Ende ist und seine Ausgabedatei schließt.

          Da Klaus Riesendateien verarbeitet (500MB), sehe ich auch die Gefahr, dass die Scriptlaufzeit überschritten wird. Müsste man den ZIP nicht eher als CRON Job realisieren?

          Rolf

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

            kann das Problem bei passthru und fpassthru sein, dass PHP gerade bei einem länger laufenden externen Programm denkt, dass keine Ausgabe mehr käme und annimmt, dass das Programm zu Ende sei?

            passthru wartet laut Doc auf einen Exit-Code...

            Also sollte es nicht abbrechen - es sei denn, PHP bricht ab.

            1. Hallo Raketenwilli,

              ja ok, bei fpassthru kann man den Exitcode nicht abrufen.

              Dafür steht dort in den Kommentaren, dass man vor fpassthru ein ob_end_clean aufrufen solle, weil große Dateien (> 5 MB) sonst nicht funktionieren würden. Sehr merkwürdig.

              Rolf

              --
              sumpsi - posui - obstruxi
          2. Da ich mit Parameter -0 zippe, also ohne Kompression, sollte der Prozess eigentlich sehr schnell sein. Und 500MB sind da noch verhältnismäßig klein. Es werden in Zukunft oft Dateien größer 1GB werden, 4,7GB waren bisher das Größte, was mir untergekommen ist.

            1. Hallo Klaus,

              4,7GB waren bisher das Größte, was mir untergekommen ist.

              hört sich verdammt nach einem DVD-Image an. 😉

              Einen schönen Tag noch
               Martin

              --
              Man ist nicht behindert, man wird behindert.
        2. Das folgende "tut es". Ich teste allerdings in der Konsole:

          <?php
          	$folder = '0815';
          	$target_path = '/tmp/test/upload/' . $folder;
          	$zip_name = $target_path . '/' . $folder . '.zip';
          	$zip_files = $target_path;
          	$sys = 'zip ' 
                   . escapeshellarg( $zip_name ) 
                   . ' -0 -j -v ' 
                   . escapeshellarg( $zip_files) 
                   . '/*';
          	echo $sys . PHP_EOL;
          	$fp = popen( $sys, 'r' );
          	
          	fpassthru( $fp );
              #exit;
          	// Dateien im Folder können jetzt gelöscht werden
          	foreach ( new DirectoryIterator( $target_path ) as $fileInfo ) {
          		if (
          				( ! $fileInfo->isDot() ) 
          			AND ( $fileInfo->getFilename() != $folder.".zip" )
          		) {
          			echo $fileInfo->getPathname() . ' soll gelöscht werden.' . PHP_EOL;
          			#unlink( $fileInfo->getPathname() );
          		}
          	}
          

          Die Zeile fpassthru( $fp ) gibt nun aus, was wie gezippt wurde und was warum nicht.

        3. Hallo Raketenwilli,

          hier die Ausgabe:

          zip '/srv/www/htdocs/test/upload/chunks/20220822T095148229Z' -0 -j -v '/srv/www/htdocs/test/upload/20220822T095148229Z/*'
          

          In allen Fällen ('popen', 'exec', 'system', 'shell_exec' erhalte ich folgende Rückmeldung:

          Status=12
          Array ( [0] => zip warning: name not matched: /srv/www/htdocs/test/upload/20220822T111343275Z/* [1] => [2] => zip error: Nothing to do! (/srv/www/htdocs/test/upload/chunks/20220822T111343275Z.zip) )
          

          Nur bei meiner Zeile ($fp = popen("zip $zip_name -0 -j -v $zip_target", "r");) erhalte ich:

          updating: Testfile.dat .... zip warning: file size changed while zipping /srv/www/htdocs/test/upload/20220822T112459423Z/Testfile.dat (in=46750000) (out=46750000) (stored 0%) total bytes=46750000, compressed=46750000 -> 0% savings

          LG Klaus

          1. Hallo Klaus1,

            ist es möglich, dass die Hochkommas, die escapeshellarg hinzufügt, die Wildcardbedeutung des Sterns eliminieren?

            Die Meldung "file size changed while zipping" deutet in der Tat darauf hin, dass die testfile.dat Datei nochmal verändert wurde, während Du schon ZIPst. Aber wie kann das sein? War der User hektisch und hat mehrere Requests zum Thema parallel abgesetzt? Oder wartet dein Client nicht von einem Ajax-Request zum nächsten, bis der vorige Request fertig ist?

            Leider gibt es wohl keine PHP Library, mit der Du ein ZIP so ähnlich wie einen Ordner im Dateisystem behandeln könntest (inclusive Open for Append). Es gibt zwar die zlib-Funktionen, aber die sind für gzip-Dateien da, also ZIPs mit nur einer Datei und ohne Inhaltsverzeichnis.

            Rolf

            --
            sumpsi - posui - obstruxi
          2. Array ( [0] => zip warning: name not matched: /srv/www/htdocs/test/upload/20220822T111343275Z/* [1] => [2] => zip error: Nothing to do! (/srv/www/htdocs/test/upload/chunks/20220822T111343275Z.zip) )

            Ja. Klar. Bei Dir ist das '/*' excaped. Das ist aber keine gute Idee, weil zip dann sauer reagiert. Mach das so wie gezeigt ...

            	$sys = 'zip ' 
                     . escapeshellarg( $zip_name ) 
                     . ' -0 -j -v ' 
                     . escapeshellarg( $zip_files) 
                     . '/*';
            

            und zip bekommt, was es will. Hier die „Schönschrift“:

            <?php
                define( 'DEBUG', true );
            	$subFolder   = '0815';
            	$target_path = '/tmp/test/upload/' . $subFolder;
            	$zip_name    = $subFolder . '.zip';
            	$zip_files   = $target_path;
            	$sys = 'zip ' 
            	     . escapeshellarg( $target_path . '/' . $zip_name ) 
            	     . ' -0 -j -v ' . escapeshellarg( $zip_files) 
            	     . '/*';
            	if ( DEBUG ) echo htmlspecialchars( $sys, ENT_NOQUOTES ) . PHP_EOL;
            	$result = system( $sys );
            	
            	if ( DEBUG ) echo 'Ausgaben:' 
            	                  . PHP_EOL 
            	                  . htmlspecialchars( $result, ENT_NOQUOTES );
                
            	foreach ( new DirectoryIterator( $target_path ) as $fileInfo ) {
            		if ( DEBUG )  echo 'Vergleiche ' 
            		                   . htmlspecialchars( $fileInfo->getFilename(), ENT_NOQUOTES )
            		                   . PHP_EOL;
            		if (
            				( ! $fileInfo->isDot() ) 
            			AND ( $fileInfo->getFilename() != $zip_name )
            		) {
            			if ( DEBUG ) echo htmlspecialchars( $fileInfo->getPathname(), ENT_NOQUOTES )
            			                  . ' soll gelöscht werden.'
            			                  . PHP_EOL;
            			if ( ! DEBUG )  unlink( $fileInfo->getPathname() );
            		}
            	}
            
            
            1. Eine Frage hab ich noch:

              man zip „sagt“:

              -m --move
              Move  the specified files into the zip archive; actually, this
              deletes the target directories/files after making the specified
              zip archive. If a directory becomes empty  after removal of the
              files, the directory is also removed.
              No deletions are done until zip has created the archive without
              error. This is useful for conserving disk space, but is
              potentially dangerous  so it is recommended to use it in
              combination with -T to test the archive before removing all input
              files.
              

              Warum also nicht zip -mT ... - Da kannst Du das ganze Löschen-Gefummel sparen.

              <?php
              define( 'DEBUG', true );
              $subFolder   = '0815';
              $target_path = '/tmp/test/upload/' . $subFolder;
              $zip_name    = $subFolder . '.zip';
              $zip_files   = $target_path;
              $sys = 'zip  ' 
              	 . escapeshellarg( $target_path . '/' . $zip_name ) 
              	 . ' -mT0jv ' . escapeshellarg( $zip_files) 
              	 . '/*';
              if ( DEBUG ) echo htmlspecialchars( $sys, ENT_NOQUOTES )
                                . PHP_EOL;
              $result = system( $sys );
              
              if ( DEBUG ) echo 'Ausgaben:' 
              				  . PHP_EOL 
              				  . htmlspecialchars( $result, ENT_NOQUOTES );
              
              
              
              
              1. Den Parameter habe ich gesucht, aber nicht gefunden. Da war ich wohl vollkommen blind, denn jetzt finde ich den Parameter auch.

                1. Der Prozess läuft jetzt auch viel schneller ab und scheint so zu funktionieren.

                  Vielen vielen Dank. Damit bin ich wieder einen riesigen Schritt weiter.

                  LG Klaus

                2. Den Parameter habe ich gesucht

                  Tippe mal in einem Terminal:

                  man zip
                  

                  In den Manuals kann man suchen:

                  • Tippe /delete um das erste Vorkommen von delete zu suchen
                  • Tippe / um das nächste Vorkommen der zuvor gesuchten Zeichenfolge zu suchen
                  • Tippe ? um das frühere Vorkommen der zuvor gesuchten Zeichenfolge zu suchen

                  Mehr zu man: man man

            2. Ich habe Dein Script quasi 1zu1 übernommen.

              Die Ausgabe (ich habe zwecks Lesbarkeit hier ein paar zeilenumbrüche eingebaut):

              zip '/srv/www/htdocs/test/upload/20220822T115538259Z/20220822T115538259Z.zip' -0 -j -v '/srv/www/htdocs/test/upload/20220822T115538259Z'/* 
              file matches zip file -- 
              skipping updating: Testfile.dat 
              zip warning: file size changed while zipping 
              /srv/www/htdocs/test/upload/20220822T115538259Z/Testfile.dat (in=9500000) (out=9500000) (stored 0%) 
              total bytes=9500000, compressed=9500000 -> 0% savings 
              Ausgaben: total bytes=9500000, compressed=9500000 -> 0% savings
              Vergleiche . Vergleiche .. Vergleiche Testfile.dat /srv/www/htdocs/test/upload/20220822T115538259Z/
              Testfile.dat soll gel�scht werden. 
              Vergleiche 20220822T115538259Z.zip
              

              Eine Zip-Datei wird nun erstellt, ist aber viel zu klein und enthält nur einen winzigen Teil der eigentlichen Datei.

              [update] Nach einer Weile (eher Minuten als Sekunden) ist die Zip-Datei bzw. die darin enthaltene Datei größer geworden und scheint tatsächlich die korrekte Datei zu enthalten.

                • quasi 1zu1
                • Eine Zip-Datei wird nun erstellt, ist aber viel zu klein
                1. Hast Du ETWA weiterhin popen() verwendet?
                2. Zeig mal das „quasi 1zu1“

                Und: WAS in Deinem Code kann zu zip warning: file size changed while zipping führen?

                Ich vermute aus dem zuvor geschriebenen, dass der Upload (mindestens) eines weiteren File-Chunks während des Zippens stattfand - die Ursache dafür liegt also außerhalb des gezeigten.

                Java-Script? Asyncron?

                1. Hier der Source-Code für das gesamte PHP-Script:

                  <? header("Content-Type: text/html; charset=utf8"); ?>
                  
                  <?
                  include "config.php";
                  
                  function guidv4($data = null) {
                      // Generate 16 bytes (128 bits) of random data or use the data passed into the function.
                      $data = $data ?? random_bytes(16);
                      assert(strlen($data) == 16);
                  
                      // Set version to 0100
                      $data[6] = chr(ord($data[6]) & 0x0f | 0x40);
                      // Set bits 6-7 to 10
                      $data[8] = chr(ord($data[8]) & 0x3f | 0x80);
                  
                      // Output the 36 character UUID.
                      return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4));
                  }
                  
                  
                  
                  $folder = $_POST["folder"];
                  $pwd = $_POST["pwd"];
                  $zippen = $_POST["zippen"];
                  $totalSize = $_POST["totalSize"];
                  
                  $verbindung = @mysqli_connect($server,$login,$pass,$dbname);
                  
                  if (!empty($folder)) {
                  	$target_path = "/srv/www/htdocs/test/upload/".$folder;
                  	$zip_path = "/srv/www/htdocs/test/upload";
                  	if ($zippen == "JA") {
                  		$zip_name    = $folder . '.zip';
                  		$zip_files   = $target_path;
                  		$sys = 'zip ' 
                  			 . escapeshellarg( $target_path . '/' . $zip_name ) 
                  			 . ' -0 -j -q -mT ' . escapeshellarg( $zip_files) 
                  			 . '/*';
                  		$result = system( $sys );
                  	}
                  	$uploader = $_SERVER["REMOTE_ADDR"];
                  	$datum = date("Y-m-d");
                  	$token = guidv4();
                  	$abfrage = "insert into transferdb (token,folder,uploader,pwd,lastaccess) values ('$token','$folder','$uploader','$pwd','$datum')";
                  	$erg = mysqli_query($verbindung, $abfrage);
                  	echo "ID=".$token;
                  }
                  
                  mysqli_close($verbindung);
                  ?>
                  
                  1. Hm.

                    Das zeigt leider nicht, ob der Upload noch stattfindet, während auf dem Server schon gezippt wird. Und die gezeigte Warnung

                    zip warning: file size changed while zipping 
                    

                    lässt genau das vermuten.

                    Es ist also sehr wahrscheinlich so, dass der Upload noch stattfindet, während gezippt werden soll.

                    Lösung:

                    • Initiere den Request für das Zippen erst, wenn die Uploads der Filechunks komplett „durch“ sind. (browserseitig, im JavaScript)
                    • Achte also auch darauf, dass der auf dem Server laufende Prozess (wohl ein anderes PHP-Skript!), der die Filechunks zur Datei zusammenfügt (auch das kann etwas dauern), sein „ok“ erst sendet, wenn wirklich alle Befehle darin abgeschlossen sind - insbesondere das Zusammenfügen der Datei wirklich vollständig stattgefunden hat.
                    $folder = $_POST["folder"];
                    

                    Ich hab jetzt nicht gesehen, dass wirklich etwas passieren kann - aber:

                    Was passiert, wenn jemand

                    ../../../../../etc
                    

                    sendet? Dann hast Du

                    $target_path = "/srv/www/htdocs/test/upload/../../../../../etc"
                    

                    oder eben:

                    $target_path = "/etc"
                    

                    Alarm!

                    Lege sowas auf dem Server fest und/oder schreibe es in eine Session. Nicht per POST oder GET vom Browser nehmen! NeverEver!

                    Das geht zwar hier aus einem anderen Grund schief (weil dann das zip-File nicht geschrieben werden kann) aber das Eis ist zu dünn.

                    1. $folder wird vom hochladenden Script erstellt (Timestamp) und zurückgegeben. Das wird dann einfach wieder an das nächste Script übergeben. Aber Du hast vollkommen recht, da muss im Anschluss noch einiges getan werden, um solche Fehlerquellen auszuschließen. Der Upload findet zwar nur durch interne eingeloggte Anwender statt und jeder Upload wird mit Loginname und IP-Adresse protokolliert, aber vorbeugen ist natürlich besser.

                      Im Javascript merke ich mir für jede Datei die Gesamtgröße und die Summe der bisher hochgeladenen Chunks. Solange es noch einen Eintrag gibt, wo die Summe kleiner als die Gesamtgröße ist, gilt der Upload noch nicht als abgeschlossen.

                      Dennoch wurde mehrmals (3 Mal) das "uploadfinished.php" aufgerufen und das Script lieferte mehrmals (13 Mal) eine ID zurück.

                      Ich habe nun zusätzlich eine globale Variable "finallydone = false" erstellt, die beim ersten Ajax-Aufruf von uploadfinished.php auf true gesetzt wird. Jetzt gibt es nur noch einen Aufruf.

                      1. Der Upload findet zwar nur durch interne eingeloggte Anwender statt

                        „Intern“ heißt: Festgelegter IP-Adressbereich oder nur weiteres Login? Und was ist mit dem Download?

                        Nicht, dass jemand 'lolita.mpg' hoch lädt und dann das Token, den Benutzername und das Passwort/die Passwörter verbreitet? So hast Du schneller die Polizei im Haus und eine (An)-Klage (StA/Zivilklage der Filmindustrie) am Hals als Du „gucken kannst“.

                        Und allein ein Ermittlungsverfahren (auch wenn es ohne Anklage beendet wird!) reicht für Unbill. Denn (wohl nicht nur in INPOL) steht - wenngleich rechtswidrig - jahrelang auch sowas) wegen KiPo sorgt dafür, dass die Polizei bei jeder Verkehrskontrolle Dein Auto zerlegt - und das ist noch der angenehmere Teil.

                        Merksatz von meinereinemselbst:

                        „Aus irgend einem Grund böswillig gewordene Mitarbeiter, Freunde oder Familienangehöriger sind die Sargnägel gutgläubiger Programmierer und Administratoren.“

                        1. Der Upload findet zwar nur durch interne eingeloggte Anwender statt

                          „Intern“ heißt: Festgelegter IP-Adressbereich oder nur weiteres Login? Und was ist mit dem Download?

                          "intern" bedeutet, dass des firmeneigene Mitarbeiter sind, die sich zuvor mit ihrem eigenen Benutzernamen und Passwort authentifiziert haben. Zusätzlich wird auch die IP-Adresse protokolliert, die über die Mac-Adresse des Rechners fest über den eigenen DHCP-Server vergeben wird. Letztlich ist der Upload auch nur aus dem internen IP-Bereich erreichbar, das heißt entweder im Hause oder über VPN.

                          Darüberhinaus stehen Auswertungen und Statistiken zur Verfügung, die anzeigen, wer was wann hochgeladen hat und wie oft von wo (IP-Adresse) heruntergeladen wurde. Uploads, auf die länger als 30 Tage nicht zugegriffen wurde, werden automatisch gelöscht.

                          Es existiert bereits so ein Transfer-Server, der aber bisher die Upload-Dateien nur aus einem Verzeichnis akzeptierte und alles in einem POST gepackt hatte. Dadurch gab es eine Begrenzung bei der maximalen Anzahl der Dateien und der maximalen Größe pro Datei. Dieser soll zukünftig ersetzt werden.

                          LG Klaus

                          1. internen IP-Bereich

                            + (Protokollierung)

                            Ok. Das reicht. Ich hatte nur Angst, dass sich jemand selbst „ein Ei legt“.