DiamondDog: Angebotener Download bricht immer ab.

Hallo Leute,
ich hab das Problem das wenn ich mit dem nachfolgenden Script einen Download starte, der 265 MB groß ist dieser immer bei um die 130 MB abgebrochen wird.
Ich hab dem Script schon extra:
@ini_set('memory_limit','1024M');
und
set_time_limit(0);
hinzugefügt aber leider ohne Erfolg.
Weiß vll noch jemand was ich machen könnte, damit der download nicht immer abbricht?
Würde gerne das direkt verlinken verhindern.

Hier mal das Script:

  
function sendFileToBrowser($filename){  
    $filelocation = $_SERVER['DOCUMENT_ROOT'].'/downloads/backups/';  
  
    if(!file_exists($filelocation . $filename))  
    	die('Die angeforderte Datei existiert nicht!');  
  
    header('Pragma: public');  
    header('Expires: 0');  
    header('Cache-Control: must-revalidate, post-check=0, pre-check=0');  
    header('Content-Transfer-Encoding: binary');  
    header('Content-Disposition: attachment; filename=' . $filename . ';');  
    header('Content-Type: application/octet-stream');  
    header('Content-Length: ' . filesize($filelocation . $filename));  
  
    $file = fopen($filelocation . $filename, "r");  
  
    @ini_set('memory_limit','1024M');  
    set_time_limit(0);  
	  
    while(!feof($file) and (connection_status()==0)) {  
        print(fread($file, 1024*8));  
        flush();  
    }  
    fclose($file);  
    die();  
}  
  
sendFileToBrowser('Datei.rar');  

Danke schon mal an alle und frohe Weihnachten.

Mfg Dog

  1. Hello,

    while(!feof($file) and (connection_status()==0)) {
            print(fread($file, 1024*8));
            flush();
        }

    Du schachtelst Funktionen. Da kannst Du nun gar nicht feststellen, welche den Fehler produziert.
    Außerdem erscheinen mir 8k-Blöcke ziemlich groß.

    Mach mal zwei Schritte aus Lesen und Schreiben und schreib immer nur 128 Bytes weg. Der Einfachheit halber würde ich dann auch immer nur 128 Bytes lesen.

    Und frag den Rückgabewert von fread ab, bevor Du print oder echo durchführst.

    Was passiert nun?

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. Hey,
      war mir nich ganz sicher beim Umsetzen mit dem was du sagtest, hoffe das ich es trozdem richtig gemacht habe und zwar hab ich es jetzt so ausprobiert:

        
          while(!feof($file) and (connection_status()==0)) {  
              while ($data = fread($file, 128))  
      		  print($data);  
              flush();  
          }  
      
      

      ansonsten bräucht ich vll nen kleinen Anstoß in die richtige Richtung.
      Aber leider führt das obere immer noch zum selber Ergebniss.

      Mfg Dog

      1. Hello,

        war mir nich ganz sicher beim Umsetzen mit dem was du sagtest, hoffe das ich es trozdem richtig gemacht habe und zwar hab ich es jetzt so ausprobiert:

        while(!feof($file) and (connection_status()==0)) {
                while ($data = fread($file, 128))
          print($data);
                flush();
            }

        
        > ansonsten bräucht ich vll nen kleinen Anstoß in die richtige Richtung.  
          
        Das ist aber schon fast richtig.  
        Den conntection\_status musst Du eigentlich auch nicht jedes Mal abfragen. Das ist nur sinnvoll, wenn Du das Script bei einem "user-abort" <http://de.php.net/manual/en/function.ignore-user-abort.php> weiterlaufen lässt und genau diese Schleife dann eben nicht weiterlaufen soll.  
          
        Anderenfalls wird bei einem User-Abort sowieso nur noch die Shutdown-Funktion ausgeführt. Alle anderen Operationen werden sofort abgebrochen und ihre Variablen aufgelöst.  
        <http://de.php.net/manual/en/function.register-shutdown-function.php>  
          
        Damit würde sich das Konstrukt reduzieren lassen auf:  
          
             while(false !== ($data = fread($file, 128)))  
             {  
                 echo $data;  
                 flush();  
             }  
          
        Hast Du mal in das Error-Log des Apachen geschaut, ob da was über den Abbruch drinsteht?  
          
        Laufen denn andere Scripte auf dem Server unendlich, wenn Du set\_time\_limit(0) benutzt? das würde ich als erstes mal ausprobieren.  
          
          
          
          
          
        Liebe Grüße aus dem schönen Oberharz  
          
          
        Tom vom Berg  
        ![](http://selfhtml.bitworks.de/Virencheck.gif)  
          
        
        -- 
         ☻\_  
        /▌  
        / \ Nur selber lernen macht schlau  
        <http://bergpost.annerschbarrich.de>
        
        1. Damit würde sich das Konstrukt reduzieren lassen auf:

          while(false !== ($data = fread($file, 128)))
               {
                   echo $data;
                   flush();
               }

          Das bringt leider auch nichts, der Download bricht immernoch ab aber nun bei um die 150 MB.

          Hast Du mal in das Error-Log des Apachen geschaut, ob da was über den Abbruch drinsteht?

          Im error_log steht leider nichts, damit schein aber was nicht zu stimmen was ich mal prüfen lassen muss.

          Laufen denn andere Scripte auf dem Server unendlich, wenn Du set_time_limit(0) benutzt? das würde ich als erstes mal ausprobieren.

          Habs mal so ausprobiert:

            
          set_time_limit ( 0 );  
            
          $x = 1;  
            
          while ( $x > 0 )  
          {  
            echo $x++ . '<br>';  
            flush();  
          }  
          
          

          Ergbniss: Browserabsturz.....

          1. Hello,

            Laufen denn andere Scripte auf dem Server unendlich, wenn Du set_time_limit(0) benutzt? das würde ich als erstes mal ausprobieren.

            Habs mal so ausprobiert:

            set_time_limit ( 0 );

            $x = 1;

            while ( $x > 0 )
            {
              echo $x++ . '<br>';
              flush();

            sleep(1);

            }

            
            > Ergbniss: Browserabsturz.....  
              
            \*grins\*  
            Vielleicht ist das beim Download dann auch ein Browserabsturz?  
              
              
              
              
            Liebe Grüße aus dem schönen Oberharz  
              
              
            Tom vom Berg  
            ![](http://selfhtml.bitworks.de/Virencheck.gif)  
              
            
            -- 
             ☻\_  
            /▌  
            / \ Nur selber lernen macht schlau  
            <http://bergpost.annerschbarrich.de>
            
            1. *grins*
              Vielleicht ist das beim Download dann auch ein Browserabsturz?

              Ne glaube ich nicht. Sobald ich das Test Script aufrufe hängt sich der Browser sofort weg. Aber der Datei Download läuft ja ne ganze Zeit ohne Probleme.

              Gibts ne Möglichkeit das vll auch zu Testen um das auch auszuschließen?

              1. Gibts ne Möglichkeit das vll auch zu Testen um das auch auszuschließen?

                Du könntet dir lokal eine Testumgebung einrichten in der du dann Zugriff auf die Logfiles hast.

                1. Du könntet dir lokal eine Testumgebung einrichten in der du dann Zugriff auf die Logfiles hast.

                  Ich glaub ich warte mal ab, biß das Problem mit meine error_log behoben wurde und dann mal sehn was dort drin steht.

                2. Edit:
                  Das hier steht nach dem Abbruch im Error_log:
                  [Sat Dec 24 18:51:33 2011] [warn] [client xx.xxx.xxx.xx] (104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server, referer: http://www.seite.de/admin/index.php
                  [Sat Dec 24 18:51:33 2011] [warn] [client xx.xxx.xxx.xx] (104)Connection reset by peer: mod_fcgid: ap_pass_brigade failed in handle_request_ipc function, referer: http://www.seite.de/admin/index.php

                  Hoffe das da weiter hilft.

  2. Hi,

    ich hab das Problem das wenn ich mit dem nachfolgenden Script einen Download starte, der 265 MB groß ist dieser immer bei um die 130 MB abgebrochen wird.
    Ich hab dem Script schon extra:
    @ini_set('memory_limit','1024M');
    und
    set_time_limit(0);
    hinzugefügt aber leider ohne Erfolg.

    Und, hast du auch kontrolliert, ob diese Einstellungen übernommen wurden?

    MfG ChrisB

    --
    RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
    1. Und, hast du auch kontrolliert, ob diese Einstellungen übernommen wurden?

      Nein hab ich nicht, wie mach ich das den?

      1. Hi,

        Und, hast du auch kontrolliert, ob diese Einstellungen übernommen wurden?
        Nein hab ich nicht, wie mach ich das den?

        weil Weihnachten ist:
        ini_get(), phpinfo()
        Nächstes Mal bitte selbst nachdenken und nachschlagen.

        Ciao,
         Martin

        --
        Niemand lebt allein von seinen Träumen.
        Aber wer träumt, lebt noch.
        Selfcode: fo:) ch:{ rl:| br:< n4:( ie:| mo:| va:) de:] zu:) fl:{ ss:) ls:µ js:(
        1. ini_get()

          hat mir geholfen so hab ich rausgefunden das:
          @ini_set('memory_limit','1024M');
          den Wert nicht verändert hat, hab es jetzt angepasst, aber noch nicht getestet.

          phpinfo()

          Hab ich zwar reingeschaut, war mir aber nicht sicher ob die Werte die da stehen aktuell mit dem Script sind.

        2. Edit:
          Also die Werte greifen jetzt aber abbrechen tut er trozdem noch.

  3. Tach!

    $file = fopen($filelocation . $filename, "r");
        while(!feof($file) and (connection_status()==0)) {
            print(fread($file, 1024*8));
            flush();
        }
        fclose($file);

    Nimm readfile() stattdessen.

    die();

    Das die() ist sinnlos, wenn es sowieso die letzte Amtshandlung der Funktion ist und nach dem Funktionsaufruf das Script endet.

    dedlfix.

    1. Nimm readfile() stattdessen.

      Wenn ich readfile nehme dann wird der download schon nach 47 Bytes abgebrochen.

      1. Hi,

        Nimm readfile() stattdessen.
        Wenn ich readfile nehme dann wird der download schon nach 47 Bytes abgebrochen.

        Das klingt eher danach, als ob du es falsch verwendet hast - und deshalb in der herruntergeladenen Datei nur eine PHP-Fehlermeldung drin steht, die eben 47 Bytes umfasst.

        MfG ChrisB

        --
        RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
        1. Hey,
          also hier mal das Script wie es jetzt ist mit readfile:

          function sendFileToBrowser($filename){  
          	$filelocation = $_SERVER['DOCUMENT_ROOT'].'/downloads/backups/';  
            
          	if(!file_exists($filelocation . $filename))  
              	die("Die angeforderte Datei existiert nicht!");  
          		  
          	if(!is_readable($filelocation . $filename))  
          		die("Die Datei kann nicht zum lesen geöffnet werden!");  
          	  
          	if(ini_get('safe_mode'))  
          		@ini_set('safe_mode', false);  
          	if(ini_get('safe_mode'))  
          		die("Der safe_mode konnte nicht abgeschaltet werden!");  
          	  
          	@ini_set('memory_limit',1024);  
          	if(ini_get('memory_limit') != 1024)  
          		die("Das memory_limit konnte nicht verändert werden! (".ini_get('memory_limit').")");  
          	  
          	@ini_set('max_execution_time', 300);  
          	if(ini_get('max_execution_time') != 300)  
          		die("Die max_execution_time konnte nicht verändert werden! (".ini_get('max_execution_time').")");  
          	  
          	set_time_limit(0);  
            
              header('Pragma: public');  
              header('Expires: 0');  
              header('Cache-Control: must-revalidate, post-check=0, pre-check=0');  
              header('Content-Transfer-Encoding: binary');  
              header('Content-Disposition: attachment; filename=' . $filename . ';');  
              header('Content-Type: application/octet-stream');  
              header('Content-Length: ' . filesize($filelocation . $filename));  
          	  
              readfile($filelocation . $filename);  
            
              /*$file = fopen($filelocation . $filename, "r");  
            
          	while(false !== ($data = fread($file, 128))){  
                   echo $data;  
          		 ob_flush();  
                   flush();  
              }  
              fclose($file);*/  
          }  
            
          sendFileToBrowser('Datei.rar');
          

          Problem wenn ich versuche es so aufzurufen bekomme ich folgende Meldung:
          ---
          Fehler: Datei nicht gefunden

          Die Dateien unter http://www.seite.de/downloads/index.php konnten nicht gefunden werden.

          Bitte überprüfen Sie die Adresse auf Rechtschreib-, Groß-/Kleinschreibungs- oder andere Fehler.

          Bitte überprüfen Sie, ob die Adresse umbenannt, gelöscht oder verschoben wurde.
          ---
          Ich weiß aber das die Datei existiert ich hab ja nur anstelle von fopen dort jetzt readfile verwendet.

          1. Tach!

            if(ini_get('safe_mode'))
            @ini_set('safe_mode', false);
            if(ini_get('safe_mode'))
            die("Der safe_mode konnte nicht abgeschaltet werden!");

            Das kann ich dir von vorn herein sagen, dass ein eventuell gesetzter Safe Mode nicht im Script abgeschaltet werden kann. Das würde die Aufgabe des Safe Mode konterkarrieren.

            @ini_set('memory_limit',1024);
            if(ini_get('memory_limit') != 1024)
            die("Das memory_limit konnte nicht verändert werden! (".ini_get('memory_limit').")");

            Mit readfile() brauchst du auch kein Gigabyte Speicher, weil dieses ebenso wie deine Schleife nur kleine Stückchen einliest und durchreicht.

            @ini_set('max_execution_time', 300);
            if(ini_get('max_execution_time') != 300)
            die("Die max_execution_time konnte nicht verändert werden! (".ini_get('max_execution_time').")");

            Neben dem PHP-internen Timeout gibt es noch Timeouts, die außerhalb konfiguriert werden. Wenn ein PHP-Timeout auftritt, bekommst du eine PHP-Fehlermeldung. Die findest du entweder in deiner Ausgabe (also am Ende der heruntergeladenen Datei) oder da wo du Fehlermeldungen explizit hingeleitet hast, wenn du das hast. Bei der max_execution_time wird auch nur die von PHP direkt verbrauchte Zeit gewertet, Zeit, die es auf das System warten muss, ist nicht in der Rechnung. Beispielswiese verbraucht bei sleep() nur der Funktionsaufruf ganz wenig dieser Zeit, egal wie lange das Script schlafen soll. Der Apache-Timeout oder ein anderer irgendwo im System konfigurierter Timeout kann aber trotzdem zuschlagen - dann aber ohen PHP-Fehlermeldung, weil PHP ja keine Chance mehr dazu hat.

            Die Dateien unter http://www.seite.de/downloads/index.php konnten nicht gefunden werden.

            Diese Meldung passt nicht zum gezeigten Code. Im Code machst du einen Dateizugriff, die Meldung redet was von einer URL, die jedoch bei einem HTTP-Request von Belang ist. Zudem ist der Meldungstext nicht im gezeigten Code zu sehen. Woher also kommt diese Meldung?

            dedlfix.

            1. if(ini\_get('safe\_mode'))  
                @ini\_set('safe\_mode', false);  
              if(ini\_get('safe\_mode'))  
                die("Der safe\_mode konnte nicht abgeschaltet werden!");  
              

              Das kann ich dir von vorn herein sagen, dass ein eventuell gesetzter Safe Mode nicht im Script abgeschaltet werden kann. Das würde die Aufgabe des Safe Mode konterkarrieren.

              Dann jetzt nur noch:

                
              if(ini_get('safe_mode'))  
              	die("Der safe_mode ist eingeschaltet abgeschaltet!");  
              
              
              @ini\_set('memory\_limit',1024);  
              if(ini\_get('memory\_limit') != 1024)  
                die("Das memory\_limit konnte nicht verändert werden! (".ini\_get('memory\_limit').")");  
              

              Mit readfile() brauchst du auch kein Gigabyte Speicher, weil dieses ebenso wie deine Schleife nur kleine Stückchen einliest und durchreicht.

              Aber wenn ich dich richtig verstehe, dann ist es in der Situation momentan egal ob die Zeile da ist oder nicht oder?

              @ini\_set('max\_execution\_time', 300);  
              if(ini\_get('max\_execution\_time') != 300)  
                die("Die max\_execution\_time konnte nicht verändert werden! (".ini\_get('max\_execution\_time').")");  
              

              Neben dem PHP-internen Timeout gibt es noch Timeouts, die außerhalb konfiguriert werden. Wenn ein PHP-Timeout auftritt, bekommst du eine PHP-Fehlermeldung. Die findest du entweder in deiner Ausgabe (also am Ende der heruntergeladenen Datei) oder da wo du Fehlermeldungen explizit hingeleitet hast, wenn du das hast. Bei der max_execution_time wird auch nur die von PHP direkt verbrauchte Zeit gewertet, Zeit, die es auf das System warten muss, ist nicht in der Rechnung. Beispielswiese verbraucht bei sleep() nur der Funktionsaufruf ganz wenig dieser Zeit, egal wie lange das Script schlafen soll. Der Apache-Timeout oder ein anderer irgendwo im System konfigurierter Timeout kann aber trotzdem zuschlagen - dann aber ohen PHP-Fehlermeldung, weil PHP ja keine Chance mehr dazu hat.

              Dazu bräuchte man glaubeich den Log um genau zu sehen wieso der Download beendet wurde.

              Die Dateien unter http://www.seite.de/downloads/index.php konnten nicht gefunden werden.

              Diese Meldung passt nicht zum gezeigten Code. Im Code machst du einen Dateizugriff, die Meldung redet was von einer URL, die jedoch bei einem HTTP-Request von Belang ist. Zudem ist der Meldungstext nicht im gezeigten Code zu sehen. Woher also kommt diese Meldung?

              Gute Frage ich hab wie gesagt nur anstelle der Schleife, das readfile eingebaut und bekomme dann diese Meldung. Klammer ich das readfile mit // aus startet er den Dateidownload sofort wieder ohne Probleme.
              *komisch*

            2. Edit:
              Hab das ganze jetzt mal etwas gekürzt:

                
              function sendFileToBrowser($filename){  
              	$filelocation = $_SERVER['DOCUMENT_ROOT'].'/downloads/backups/';  
                
                  header('Content-Type: application/octet-stream');  
              	header('Cache-Control: must-revalidate, post-check=0, pre-check=0');  
                  header('Content-Disposition: attachment; filename=' . $filename . ';');  
              	header('Content-Length: ' . filesize($filelocation . $filename));  
                
                  readfile($dir.$file);  
              }  
                
              sendFileToBrowser('Datei.rar')  
              
              

              Dann kann ich die Datei zwar downloaden, aber Sie ist dann immer 0 Bytes groß.

              1. Hi,

                     header('Content-Disposition: attachment; filename=' . $filename . ';');  
                	header('Content-Length: ' . filesize($filelocation . $filename));  
                  
                    readfile($dir.$file);
                
                  
                Handelt es sich hier bei den zwei Dateipfad-Namen-Kombinationen, die du an diesen beiden Stellen verwendest, um die selben?  
                Falls ja, wieso baust du sie aus jeweils unterschiedlichen Variablen zusammen?  
                Falls nein - erscheint dir das nicht selber etwas blödsinnig, die Content-Length einer anderen Datei zu ermitteln als der, die letztendlich an den Client ausgegeben wird?  
                  
                MfG ChrisB  
                  
                
                -- 
                RGB is totally confusing - I mean, at least #C0FFEE should be brown, right?
                
                1. Handelt es sich hier bei den zwei Dateipfad-Namen-Kombinationen, die du an diesen beiden Stellen verwendest, um die selben?
                  Falls ja, wieso baust du sie aus jeweils unterschiedlichen Variablen zusammen?
                  Falls nein - erscheint dir das nicht selber etwas blödsinnig, die Content-Length einer anderen Datei zu ermitteln als der, die letztendlich an den Client ausgegeben wird?

                  Das war ein Fehler von meiner Seite, sorry dafür so sollte es aussehen:
                  readfile($filelocation . $filename);
                  das führt dann aber wieder zu der "Fehler: Datei nicht gefunden" Seite.

  4. Hello,

    auf welchem System läuft der Apache?

    Wenn es ein WinDOS-Derivat ist, könnte es auch an einem mandantory locking liegen, also dass ein anderer Prozess die Datei festhält oder zwischendurch für sich exclusiv öffnet.

    Liebe Grüße aus dem schönen Oberharz

    Tom vom Berg

    --
     ☻_
    /▌
    / \ Nur selber lernen macht schlau
    http://bergpost.annerschbarrich.de
    1. auf welchem System läuft der Apache?

      Wenn es ein WinDOS-Derivat ist, könnte es auch an einem mandantory locking liegen, also dass ein anderer Prozess die Datei festhält oder zwischendurch für sich exclusiv öffnet.

      Wenn ich ehrlich bin hab ich keine Ahnung, kenn mich damit leider nicht wirklich aus ich hab nen Webspace soviel kann ich sagen. :S

  5. Hallo Leute,
    ich hab das Problem

    Moin!

    Die Kernfrage ist, warum Du überhaupt PHP benutzt um eine statische Datei zum Download anzubieten. Überlege ob der Nutzen die Nachteile überwiegt.

    Wenn Dein Download 265 MB groß ist, dann wäre es sehr viel schlauer das den Webserver selbst machen zu lassen. Denke allein mal an Downloadmanager oder ein simples "wget -c $URL", welche abgebrochene Downloads fortsetzen können.

    Ich sehe bei Deiner Frage keinerlei Ausgaben des error-logs. Das wäre hier erste Pflicht.

    Ich sehe in Deinem Code keinerlei Prüfung ob die angegebene Datei existiert. Das ein einfaches file_exists(), is_file(), is_readable() könnte Deine, im Thread weiter unten auftauchenden Probleme (insbesondere 0 Byte Dateigröße des Downloads!) ganz einfach offenkundig werden lassen.

    Fred

      Die Kernfrage ist, warum Du überhaupt PHP benutzt um eine statische Datei zum Download anzubieten. Überlege ob der Nutzen die Nachteile überwiegt.

      Um das direkt Linken zu verhindern.

      Wenn Dein Download 265 MB groß ist, dann wäre es sehr viel schlauer das den Webserver selbst machen zu lassen. Denke allein mal an Downloadmanager oder ein simples "wget -c $URL", welche abgebrochene Downloads fortsetzen können.

      Also per direkt Link richtig?

      Ich sehe bei Deiner Frage keinerlei Ausgaben des error-logs. Das wäre hier erste Pflicht.

      Mitlerweile funktioniert der error_log:
      [Sat Dec 24 18:51:33 2011] [warn] [client xx.xxx.xxx.xx] (104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server, referer: http://www.seite.de/admin/index.php
      [Sat Dec 24 18:51:33 2011] [warn] [client xx.xxx.xxx.xx] (104)Connection reset by peer: mod_fcgid: ap_pass_brigade failed in handle_request_ipc function, referer: http://www.seite.de/admin/index.php

      Ich sehe in Deinem Code keinerlei Prüfung ob die angegebene Datei existiert. Das ein einfaches file_exists(), is_file(), is_readable() könnte Deine, im Thread weiter unten auftauchenden Probleme (insbesondere 0 Byte Dateigröße des Downloads!) ganz einfach offenkundig werden lassen.

      Also das File existiert, hab ich später im Script alles eingebaut.

      1. Hello,

        Mittlerweile funktioniert der error_log:
        [Sat Dec 24 18:51:33 2011] [warn] [client xx.xxx.xxx.xx] (104)Connection reset by peer: mod_fcgid: error reading data from FastCGI server, referer: http://www.seite.de/admin/index.php
        [Sat Dec 24 18:51:33 2011] [warn] [client xx.xxx.xxx.xx] (104)Connection reset by peer: mod_fcgid: ap_pass_brigade failed in handle_request_ipc function, referer: http://www.seite.de/admin/index.php

        Da scheinst Du nicht der einzige zu sein, der mit PHP für FastCGI diese Probleme hat. Ich habe gestern noch ein wenig gelesen, aber leider auch nicht wirklich Ursache und Lösung gefunden. Es könnte sich um einen Bug vom modfcgid handeln.

        Ich sehe in Deinem Code keinerlei Prüfung ob die angegebene Datei existiert. Das ein einfaches file_exists(), is_file(), is_readable() könnte Deine, im Thread weiter unten auftauchenden Probleme (insbesondere 0 Byte Dateigröße des Downloads!) ganz einfach offenkundig werden lassen.

        Wieso, er benutzt doch ein fopen(). Wenn das File nicht existiert, müsste es eine Fehlermeldung geben. Und wenn das File dann noch gesperrt sein sollte (allerdings durch mandatory Locking auf WinDOSen), würde ein fread() auch einen Fehler produzieren.

        @DiamondDog:

        Ein Workaround, um die Gefahr des direkten Linkens zu vermindern:
        Kopiere das File unter einem Zufallsnamen in ein speielles Verzeichnis und linke auf diesen Zufallsnamen.

        Von Zeit zu Zeit löschst Du dann alle Files in diesem Verzeichnis. Das kannst Du so ähnlich machen, wie der Garbage Collector von PHP, also z.B. bei jedem zehnten Mal Reinkopieren wird das Verzeichnis gescannt auf Files, die älter sind als z.B. eine Stunde und die werden dann gelöscht.

        Um die Zeitabfrage billiger zu machen, kannst Du den Timestamp auch vor den Zufallsnamen setzen.

        So kann das Ausliefern dann vom Server direkt geschehen, der dann auch Chunked arbeiten kann. Ob das direkte Ausliefern einwandfrei funktioniert, würde ich aber vorher nochmal genau ausprobieren, z.B. mit einem großen PDF-File. Der Adobe-PDF-Reader arbeitet z.B. chunked, wenn er es angeboten bekommt.

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
         ☻_
        /▌
        / \ Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
        1. Ein Workaround, um die Gefahr des direkten Linkens zu vermindern:
          Kopiere das File unter einem Zufallsnamen in ein speielles Verzeichnis und linke auf diesen Zufallsnamen.

          Von Zeit zu Zeit löschst Du dann alle Files in diesem Verzeichnis. Das kannst Du so ähnlich machen, wie der Garbage Collector von PHP, also z.B. bei jedem zehnten Mal Reinkopieren wird das Verzeichnis gescannt auf Files, die älter sind als z.B. eine Stunde und die werden dann gelöscht.

          Um die Zeitabfrage billiger zu machen, kannst Du den Timestamp auch vor den Zufallsnamen setzen.

          So kann das Ausliefern dann vom Server direkt geschehen, der dann auch Chunked arbeiten kann. Ob das direkte Ausliefern einwandfrei funktioniert, würde ich aber vorher nochmal genau ausprobieren, z.B. mit einem großen PDF-File. Der Adobe-PDF-Reader arbeitet z.B. chunked, wenn er es angeboten bekommt.

          Aber führt das nich zu extrem hohem Trafik, wenn ich die Files immer kopiere?
          Aber es wär ne Möglichkeit die Files mit ner Timestamp zu kopieren und alles was älter als 24 Stunden is wird gelöscht. Man könnte zudem dann ja vorher noch prüfen ob das File schon kopiert wurde und die Timestamp im Name des Files auf 0 setzen so das das File wieder 24 Studen zur Verfügung stände.

          Werd mal sehn, was ich da so zu zaubern kann.

          1. Hello,

            Ein Workaround, um die Gefahr des direkten Linkens zu vermindern:
            Kopiere das File unter einem Zufallsnamen in ein spezielles Verzeichnis und linke auf diesen Zufallsnamen.

            Von Zeit zu Zeit löschst Du dann alle Files in diesem Verzeichnis. Das kannst Du so ähnlich machen, wie der Garbage Collector von PHP, also z.B. bei jedem zehnten Mal Reinkopieren wird das Verzeichnis gescannt auf Files, die älter sind als z.B. eine Stunde und die werden dann gelöscht.

            Um die Zeitabfrage billiger zu machen, kannst Du den Timestamp auch vor den Zufallsnamen setzen.

            So kann das Ausliefern dann vom Server direkt geschehen, der dann auch Chunked arbeiten kann. Ob das direkte Ausliefern einwandfrei funktioniert, würde ich aber vorher nochmal genau ausprobieren, z.B. mit einem großen PDF-File. Der Adobe-PDF-Reader arbeitet z.B. chunked, wenn er es angeboten bekommt.

            Aber führt das nich zu extrem hohem Trafik, wenn ich die Files immer kopiere?

            Das findet doch nur auf Anforderung im Filesystem des Servers intern statt.

            Aber es wär ne Möglichkeit die Files mit ner Timestamp zu kopieren und alles was älter als 24 Stunden is wird gelöscht. Man könnte zudem dann ja vorher noch prüfen ob das File schon kopiert wurde und die Timestamp im Name des Files auf 0 setzen so das das File wieder 24 Studen zur Verfügung stände.

            1. Server sendet Seite mit Formular, auf dem Files zum Download angeboten werden
            2. Client sendet angehaktes Formular an Server
            3. Server nimmt die dazu passend in der Session hinterlegten Filenamen und macht ein
               Zip-Archiv daraus. Dieses stellt er in einem Verzeichnis unter einem Key ([timestamp].[key].zip)
               zur Verfügung und sendet dem Client einen Relocation-Header auf diese Ressource
            4. Client lädt die Ressource herunter und speichert sie oder macht sonstwas damit

            Bei jedem (x-ten) Aufruf des Anforderungsscriptes prüft der Server, ob es Files gibt im Downloadverzeichnis, die älter als z.B. 10 Minuten sind und löscht diese.

            Für das Downloadverzeichnis müssen selbstverständlich die Indexes ausgeschaltet sein, also

            options -indexes

            in die .htaccess oder besser in die Virtual-Host-Konfiguration für dieses Verzeichnis eintragen.

            Ob der Client das/die File(s) tatsächlich heruntergeladen hat, kannst Du so zwar nicht feststellen. Das erkennst Du aus den Logs. Aber ob er sie angefordert hat, kannst Du sehen. Und wenn jedem Client nur "seine" Files angeboten werden, dann kann er auch keine anderen herunterladen.

            Am besten sit es, beim Aufruf des Angebotsformulares schon die Zugriffsprüfung zu machen und dem und die resultierende Liste in die Session einzutragen. Dem Client sendest Du nicht den wahren Namen, sondern nur den Index aus der Session zum Auswählen. Files, die nicht in der Session stehen, können nicht heruntergeladen werden.

            Liebe Grüße aus dem schönen Oberharz

            Tom vom Berg

            --
             ☻_
            /▌
            / \ Nur selber lernen macht schlau
            http://bergpost.annerschbarrich.de
            1. Grüße,
              kann man nicht stattdess an modrewrite-ähnlichem prinzip schrauben?
              als laie hätte ich versucht die .htaccess neu zu generieren - ich meine, der nutzer muss ja nicht wirklich wissen, wie die datei heißt? oder red ich grad schmarrrn?
              MFG
              bleicher

              --
              __________________________-

              FirefoxMyth
      2. Also per direkt Link richtig?

        Ja. Setze dem Besucher wonach auch immer(*) ein Cookie und leite ihn zum Download weiter. Hat er beim Abruf der Ressource das Cookie nicht, so schick ihn in die Wüste.

        (*) oder überlege an einer generellen Authentifizierung mit htaccess. Leider wird beides auch die Benutzung von Downloadmanagern unmöglich machen oder den fortgesetzten  (-> wget -c) erschweren.

        fred

        1. Hello Fred,

          Also per direkt Link richtig?

          Ja. Setze dem Besucher wonach auch immer(*) ein Cookie und leite ihn zum Download weiter. Hat er beim Abruf der Ressource das Cookie nicht, so schick ihn in die Wüste.

          Kannst Du das bitte mal etwas ausführlicher ausführen, wie das geht?

          Wie erkennt der Server, welches Cookie jetzt gerade gültig ist und für welche Files es gilt?

          Files, die nicht zum Cookie gehören, sollten ja gar nicht erst zum Download angeboten werden und wenn sie dennoch angefordert werden, sollten der Download abgelehnt werden.

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
           ☻_
          /▌
          / \ Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
          1. Hallo Tom vom Berg.

            Ja. Setze dem Besucher wonach auch immer(*) ein Cookie und leite ihn zum Download weiter. Hat er beim Abruf der Ressource das Cookie nicht, so schick ihn in die Wüste.

            Kannst Du das bitte mal etwas ausführlicher ausführen, wie das geht?

            Zu erst will ich wissen welche Sicherheit erforderlich ist. Geht es darum vor dem Download lediglich eine weiche Abfrage zu haben (z.B. um Mailadressen einzusammeln...) oder darum, dass in irgendeiner Form sicher gestellt werden soll dass nur Berechtigte die Datei laden?

            Wie erkennt der Server, welches Cookie jetzt gerade gültig ist und für welche Files es gilt?

            Stichwort Manipulation der .htaccess. Wer dynamische Webseiten über Templates generieren kann, der kann genau so auch die .htaccess manipulieren und aufräumen.

            Files, die nicht zum Cookie gehören, sollten ja gar nicht erst zum Download angeboten werden und wenn sie dennoch angefordert werden, sollten der Download abgelehnt werden.

            Ersteres würde ich dem Skript überlassen in dem die zuvor einzugebenden Daten erfasst werden, letzteres mod_rewrite.

            1. Hello,

              Kannst Du das bitte mal etwas ausführlicher ausführen, wie das geht?
              Wie erkennt der Server, welches Cookie jetzt gerade gültig ist und für welche Files es gilt?

              Stichwort Manipulation der .htaccess. Wer dynamische Webseiten über Templates generieren kann, der kann genau so auch die .htaccess manipulieren und aufräumen.

              Na, dann kann man die User ja gleich in Gruppen einteilen und die Files von der Gruppenzugehörigkeit abhängig machen.

              Ich bin mir aber im Moment nicht sicher, ob <files> oder <filesMatch> mit require zusammenarbeitet.

              Wie verknüpfen wir aber jetzt den Downloadlink mit der Ressource? Der Downloadlink muss noch in einer ungeschützten Seite liegen, die Ressource in der durch .htaccess geschützten.

              Es wird also auch wieder ein Stück Script benötigt, dass die Linkliste für den jeweiligen User erstellt. Der authentifiziert sich aber nun erst nach dem Aufruf des Hyperlinks. Das bedeutet, dass die Linkliste ganz viele Links auf Files enthält, die für diesen User nicht zugänglich sein sollen.

              Abhilfe würde nur redundante Arbeit leisten. Oder siehst Du eine Möglichkeit, die Angaben für <files ...> aus einer Textdatei zu beziehen?

              Oder gleich für jede Gruppe ein eigenes Verzeichnis anlegen und nur mit Verzeichnisschutz arbeiten?

              Aber das wolltest Du uns ja eigentlich alles auseinanderklamüsern, oder?  ;-P

              Liebe Grüße aus dem schönen Oberharz

              Tom vom Berg

              --
               ☻_
              /▌
              / \ Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
              1. Aber das wolltest Du uns ja eigentlich alles auseinanderklamüsern, oder?  ;-P

                Ok... weil Weihnachten ist - hier die weiche Variante, welche keine wirkliche Sicherheit bietet. Ist aber ideal um ein direktes Linken zu verhindern.

                Voraussetzung:

                Die eigentliche, statisch vorhandene Datei liegt in einem Verzeichnis, das außerhalb von Document-Root liegt oder mit htaccess geschützt ist (deny from all).

                Der Benutzer ruft ein Formular auf und sendet Daten.

                Die Daten werden geprüft. Im Falle des Fehlers geht es zurück zum Formular.

                Ab hier also nur im positiven Fall, es folgt Pseudocode, der an PHP angelehnt ist.

                # Baue einen Zeitstempel:  
                $zeitstempel=date('YmdHi'). # 201125122039  
                  
                # (sonst leeres) Verzeichnis für die Links:  
                $verzeichnis_mit_Links_local = '/var/www/vhosts/vhost_1/downloads';  
                $verzeichnis_mit_Links_http  = '/downloads';  
                  
                # Suche nach Dateien, die zu alt sind und löschen derselben:  
                  
                $arDateien = scandir($verzeichnis_mit_Links_local);  
                foreach ($arDateien as $strDatei) {  
                    if ('.' != $strDatei{1} {  
                        $arTmp = explode('--', $strDatei);  
                        if  ($arTmp[0] < ($zeitstempel-1) ) {  
                           unlink ($verzeichnis_mit_Links . '/' . $strDatei);  
                        }  
                    }  
                }  
                # Fertig, der Schrott ist gelöscht.  
                  
                #Baue einen Dateiname, der den Zeitstempel beeinhaltet:  
                $linkfilename = $zeitstempel . '--' . md5(date('YmdHis') . $rand(0,get_randmax()) . 'das ist ein Salt') . '.rar';  
                  
                $linkname_http  = $verzeichnis_mit_Links_http  . '/' . $linkfilename;  
                $linkname_local = $verzeichnis_mit_Links_local . '/' . $linkfilename;  
                  
                #Die Quelle:  
                $quelle = '/var/www/vhosts/vhost_1/privat/ganz_geheime_datei.rar';  
                  
                # Verlinke die Datei:  
                link ($quelle, $linkname);  
                  
                # leite den Benutzer weiter:  
                header('Location: ' . $_SERVER['HTTP_HOST'] . $linkname_http);  
                  
                # fertig:  
                exit;
                

                Die .htaccess des Downloadverzeichnisses:

                # Abschalten der Indizierung, hoffentlich erlaubt  
                Options -Indexes  
                  
                # speziellen Dateiname und Typ senden, mode_header ist hoffentlich geladen:  
                [link:http://httpd.apache.org/docs/2.0/mod/mod_headers.html@title=Header] set Content-Type        "application/x-rar-compressed"  
                Header set Content-Disposition "attachment; filename=download.rar"
                

                Wenn es nicht geht dann liegt das ohne Zweifel daran, dass der Pseudocode ohne zu überlegen abgeschrieben wurde.

                Hinweis: Es macht überhaupt nichts aus, wenn der Link gelöscht wird, so lange der Download noch läuft. Das begonnene Lesen in der Date bleibt möglich so lange die eigentlichen Daten nicht gelöscht oder überschrieben werden. Natürlich kann man die Links auch länger leben lassen. Das Anlegen eines links frisst weder extra Speicher noch nennenswert Zeit.

                Diese Methode ist nicht zu empfehlen, wenn das Dateisystem NTFS oder noch schlechter ist und nicht möglich (wg. htaccess) wenn der Server kein Apache ist. Mit Apache auf Linux, Solaris, BSD oder sonstigen unixartigen OS sollte die Vorgehensweise passen.

                Fred