php download funktion mit multipart und resume

April 30th, 2006 by admin

die funktion erlaubt downloadbeschleuniger(flashget, getright usw.) einzusetzen und den download zu resumen. zusätzlich kann nach abbruch oder fertigstellung des downloads eine funktion ausgeführt werden. diese könnte z.b. den erzeugten traffic abspeichern. wer mit der $chunksize und dem sleep(1) experimentiert kann die downloadgeschwindigkeit beliebig anpassen.

PHP:
  1. function output_file($file,$name)
  2. {
  3. //register_shutdown_function( 'function_name'  );//do something on download abort/finish
  4. if(!file_exists($file))
  5. die('file not exist!');
  6. $size = filesize($file);
  7. $name = rawurldecode($name);
  8.  
  9. if (ereg('Opera(/| )([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
  10. $UserBrowser = "Opera";
  11. elseif (ereg('MSIE ([0-9].[0-9]{1,2})', $_SERVER['HTTP_USER_AGENT']))
  12. $UserBrowser = "IE";
  13. else
  14. $UserBrowser = '';
  15. $mime_type = ($UserBrowser == 'IE' || $UserBrowser == 'Opera') ? 'application/octetstream' : 'application/octet-stream';/// important for download
  16. @ob_end_clean(); /// decrease cpu usage extreme
  17. header('Content-Type: ' . $mime_type);
  18. header('Content-Disposition: attachment; filename="'.$name.'"');
  19. header ('Expires: '.gmdate("D, d M Y H:i:s", mktime(date("H")+2, date("i"), date("s"), date("m"), date("d"), date("Y"))).' GMT');
  20. header ('Accept-Ranges: bytes');
  21. header("Cache-control: private");
  22. header ('Pragma: private');
  23. /////  multipart-download and resume-download
  24. if(isset($_SERVER['HTTP_RANGE']))
  25. {
  26. list($a, $range) = explode("=",$_SERVER['HTTP_RANGE']);
  27. str_replace($range, "-", $range);
  28. $size2 = $size-1;
  29. $new_length = $size-$range;
  30. header("HTTP/1.1 206 Partial Content");
  31. header("Content-Length: $new_length");
  32. header("Content-Range: bytes $range$size2/$size");
  33. }
  34. else
  35. {
  36. $size2=$size-1;
  37. header("Content-Length: ".$size);
  38. }
  39. $chunksize = 1*(1024*1024);
  40. $this->bytes_send = 0;
  41. if ($file = fopen($file, 'r'))
  42. {
  43. if(isset($_SERVER['HTTP_RANGE']))
  44. fseek($file, $range);
  45. while(!feof($file) and (connection_status()==0))
  46. {
  47. $buffer = fread($file, $chunksize);
  48. print($buffer);//echo($buffer); // is also possible
  49. $this->bytes_send += strlen($buffer);
  50. //sleep(1);//// decrease download speed
  51. }
  52. fclose($file);
  53. }
  54. else
  55. die('error can not open file');
  56. if(isset($new_length))
  57. $size = $new_length;
  58. die();
  59. }

Posted in php |

12 Responses

  1. ICh Says:

    Hallo, $this->bytes_send mag er bei mir nicht, sowie wird nach dem download keine weiter funktion ausgeführt..
    nach flush(); ist leider schluß.. aber danach sollte es weitergehen..

  2. $_SELF Says:

    @ICh
    mach $bytes_send draus

  3. mh166 Says:

    Danke für diese Funktion, des is echt genial, das Teil. :)

    Aber hab ma 2 Fragen:
    Die Variable $bytes_send hat doch anscheinend (für die Funktionalität an sich) keinen Zweck — wozu isn die dann dort?

    Zweitens: Bei Fehlern sollte man da nicht lieber die HTTP-Statuscodes zurückgeben, statt einfach mit die() zu beenden?
    D.h. bei “die(’file not exist!’);” den Code 404 und bei “die(’error can not open file’);” den Code 505 zurückgeben.

    Und zu guter letzt: Was bringt diese Frage, ob $new_length gesetzt is, ab Zeile 57? Hat doch eigentlich nicht mehr große Auswirkungen auf den Download, oder täusch ich mich?

    mfg, mh166

  4. admin Says:

    1. $bytes_send kannst du nutzen um z.b. in einer mysql db die runtergeladenen mb von einem bestimmten nutzer zu zählen (like rapidshare) dabei verweise ich einfach mal auf register_shutdown_function.
    2. das ist richtig. aber für meine zwecke benötige ich soetwas nicht.
    3. die variable new_length hat eigentlich nur etwas mit dem header bei multipartdownloads zu tun und kann somit bei normalen downloads vernachlässigt werden. da die funktion eigentlich eine methode aus einer download-klasse ist kann es sein das ich die variable noch für andere zwecke benötige (müßte ich aber im source code nachschauen).
    hoffe das reicht dir an antworten. wenn nicht einfach weiter fragen.

  5. MrAnderson Says:

    Erst einmal großes lob, nach so etwas suche ich schon länger.
    Kann es sein, dass das Firefox Plugin DownloadThemAll mit der Funktion nicht richtig klar kommt? Mir ist auf gefallen, dass DTA zuerst wie gewollt die Datei in X Teilen runter läd, aber dann die ganze Datei noch einmal am Stück.
    Beim FreeDownloadManager, den ich auch noch getestet habe, tritt dieser Fehler nicht auf. Der FDM benötigt aber um das Teilstück 2 zu laden fast immer zwei Anläufe.
    Wollte nur einmal nachfragen, ob dieses Problem, bei anderen auch auftritt.

  6. admin Says:

    ich hab es damals mit dem firefox 1, flashget, und ie getestet.
    bei mir hat alles funktioniert.

    wenn ich ein wenig zeit hab werd ich mir das mal genau anschauen.

  7. bla Says:

    Was genau soll denn das machen?

    str_replace($range, “-”, $range);

    ???
    Ist doch irgendwie sinnlos, weil der zurückgegebene Wert nirgendwo gespeichert wird.

  8. admin Says:

    da haste eindeutig recht. könnte sein das ich das mal zum testen hatte. kannste natürlich löschen :-)

  9. bla Says:

    header(”Content-Range: bytes $range$size2/$size”);

    Und das? *g*

    Wenn $range == 0-500 ist und die Datei 1000 Bytes groß ist, kriegt der Browser sowas wie

    Content-Range: bytes 0-500999/1000

    geliefert. Mal davon abgesehen, dass ich nicht weiß, was in

    $new_length = $size-$range;

    passiert, wenn $range die Zeichenkette ‘0-500′ ist. Könnte der Grund sein, warum das Script bei mir mit Downloadmanagern auch nicht *so* wirklich funktioniert.

  10. bla Says:

    Mach’s mal lieber so (Variablen sind aber ein bisschen anders):

    $size = filesize($file);
    $end = $size - 1;
    if (isset($_SERVER['HTTP_RANGE'])) {
    list($dummy, $range) = explode(’=', $_SERVER['HTTP_RANGE']);
    list($start, $dummy) = explode(’-', $range);
    $length = $size - $start;
    header(’HTTP/1.1 206 Partial Content’);
    } else {
    $start = 0;
    $length = $size;
    }
    header(’Content-Length: ‘.$length);
    header(”Content-Range: bytes $start-$end/$size”);

    und dann unten halt Datei öffnen und

    fseek($file, $start);

    Einziges Manko ist jetzt noch, dass das Script immer den kompletten Rest schickt, auch wenn der Endwert in $range gar nicht das Dateiende ist.

  11. admin Says:

    hast du “Content-Range: bytes 0-500999/1000″ mit eigenen augen gesehen oder denkst du das es diese werte bekommt?

    ich hätte es sonnst nicht so gemacht.
    bitte versuche es einmal mit flashget dort müßtest du all gesendeten header sehen können.

    und wie gesagt bei mir funktioniert es.

  12. Bla Says:

    Nachdem Download Accelerator nur korrupte Daten lieferte und ein FF mit “DownThemAll”-Plugin in einer Endlosschleife(!) anfing, die Datei zu ziehen, habe ich mir die Aussage angesehen.
    Kann ja sein, dass FlashGet zurechtkommt. Die beiden anderen wollten jedenfalls nicht.

Leave a Comment

Please note: Comment moderation is enabled and may delay your comment. There is no need to resubmit your comment.