1. <?php
  2. function send_file_to_client($real_filename, $filename, $disposition = null, $send_name = false, $delete = false) {
  3. if (!file_exists($real_filename)) {
  4. return false;
  5. }
  6. $content_type = get_mime_type($filename);
  7. if ($content_type == 'text/html') {
  8. $charset = '; charset=' . html_charset($real_filename);
  9. } elseif ($content_type == 'text/plain') {
  10. $charset = '; charset=' . text_charset($real_filename);
  11. } else {
  12. $charset = '';
  13. }
  14. if ($send_name) {
  15. if (preg_match('/[^\x20-\x7E]/', $filename) and
  16. strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE') !== false) {
  17. $filename = urlencode($filename);
  18. }
  19. // Add quotes to filename if it contains spaces
  20. if (strpos($filename, ' ') !== false) {
  21. $filename = '"' . $filename . '"';
  22. }
  23. $filenameattr = '; filename=' . $filename;
  24. if (!isset($disposition)) {
  25. $disposition = 'attachment';
  26. }
  27. } else {
  28. $filenameattr = '';
  29. }
  30. header("Content-type: $content_type$charset");
  31. if (isset($disposition)) {
  32. header("Content-Disposition: $disposition$filenameattr");
  33. }
  34. header('Pragma:');
  35. header('Cache-Control: public');
  36. $mtime = filemtime($real_filename);
  37. $mdate = gmdate('D, d M Y H:i:s', $mtime);
  38. $etag = md5($real_filename . $mdate . $filename . filesize($real_filename));
  39. header('Last-Modified: ' . $mdate . ' GMT');
  40. header("Etag: $etag");
  41. if ((array_key_exists('HTTP_IF_MODIFIED_SINCE', $_SERVER) and
  42. strtotime(preg_replace('/;.*$/', '', $_SERVER['HTTP_IF_MODIFIED_SINCE'])) >= $mtime) or
  43. (array_key_exists('HTTP_IF_NONE_MATCH', $_SERVER) and
  44. trim($_SERVER['HTTP_IF_NONE_MATCH']) == $etag)) {
  45. header("HTTP/1.0 304 Not Modified");
  46. } else {
  47. if ($delete) {
  48. register_shutdown_function('unlink', $real_filename);
  49. }
  50. $size = filesize($real_filename);
  51. if(isset($_SERVER['HTTP_RANGE'])) {
  52. // error_log('http range ON: ' . $_SERVER['HTTP_RANGE']); // debug output in apache error.log
  53. // Parse the range header to get the byte offset
  54. $ranges = array_map(
  55. 'intval', // Parse the parts into integer
  56. explode(
  57. '-', // The range separator
  58. substr($_SERVER['HTTP_RANGE'], 6) // Skip the `bytes=` part of the header
  59. )
  60. );
  61. if (!$ranges[1]) { // Second number missing, return from byte $range[0] to end
  62. $start = $ranges[0];
  63. $end = $size - 1;
  64. } else { // Both numbers present, return specific range
  65. $start = $ranges[0];
  66. $end = $ranges[1];
  67. }
  68. $length = $end - $start + 1;
  69. // Send the appropriate headers
  70. header('HTTP/1.1 206 Partial Content');
  71. header('Accept-Ranges: bytes');
  72. header('Content-Length: ' . $length);
  73. header(
  74. sprintf(
  75. 'Content-Range: bytes %d-%d/%d', // The header format
  76. $start, // The start range
  77. $end, // The end range
  78. $size // Total size of the file
  79. )
  80. );
  81. $f = fopen($real_filename, 'rb'); // Open the file in binary mode
  82. $chunkSize = 8192; // The size of each chunk to output
  83. fseek($f, $start); // Seek to the requested start range
  84. stop_output_buffering();
  85. while ($length) { // Read in blocks of chunksize so we don't chew up memory on the server
  86. $read = ($length > $chunkSize) ? $chunkSize : $length;
  87. $length -= $read;
  88. echo fread($f, $read);
  89. }
  90. fclose($f);
  91. } else {
  92. // error_log('http range OFF'); // debug output in apache error.log
  93. header('Content-length: ' . $size);
  94. stop_output_buffering();
  95. readfile($real_filename);
  96. }
  97. }
  98. return true;
  99. }
  100. ?>
Comments powered by Disqus