Created
October 7, 2018 16:08
-
-
Save kalamun/fb7b08c222bd6554f813ae938aecb087 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Load local files and stream it chunked | |
* Tested also with media and large files | |
*/ | |
function stream_file( $file, $is_resume = true ) | |
{ | |
//First, see if the file exists | |
if (!is_file($file)) | |
{ | |
header('HTTP/1.0 404 Not found'); | |
die("File not found"); | |
} | |
//Gather relevent info about file | |
$size = filesize($file); | |
$fileinfo = pathinfo($file); | |
$chunk_length = $size; | |
//workaround for IE filename bug with multiple periods / multiple dots in filename | |
//that adds square brackets to filename - eg. setup.abc.exe becomes setup[1].abc.exe | |
$filename = (strstr($_SERVER['HTTP_USER_AGENT'], 'MSIE')) ? | |
preg_replace('/\./', '%2e', $fileinfo['basename'], substr_count($fileinfo['basename'], '.') - 1) : | |
$fileinfo['basename']; | |
// get mime type | |
$finfo = finfo_open( FILEINFO_MIME_TYPE ); | |
$mime_type = finfo_file( $finfo, $file ); | |
finfo_close( $finfo ); | |
//check if http_range is sent by browser (or download manager) | |
if($is_resume && isset( $_SERVER['HTTP_RANGE'] ) ) | |
{ | |
list( $size_unit, $range_orig ) = explode( '=', $_SERVER['HTTP_RANGE'], 2 ); | |
if( $size_unit == 'bytes' ) | |
{ | |
//multiple ranges could be specified at the same time, but for simplicity only serve the first range | |
//http://tools.ietf.org/id/draft-ietf-http-range-retrieval-00.txt | |
list( $range, $extra_ranges ) = explode( ',', $range_orig, 2 ); | |
} | |
else | |
{ | |
$range = ''; | |
} | |
//figure out download piece from range (if set) | |
list( $seek_start, $seek_end ) = explode( '-', $range, 2 ); | |
} else { | |
$range = ''; | |
} | |
//set start and end based on range (if set), else set defaults | |
//also check for invalid ranges. | |
$seek_end = ( empty( $seek_end ) ) ? ($size - 1) : min(abs( intval( $seek_end ) ), ( $size - 1 ) ); | |
$seek_start = ( empty( $seek_start ) || $seek_end < abs( intval( $seek_start ) ) ) ? 0 : max( abs( intval( $seek_start ) ), 0 ); | |
//add headers if resumable | |
if( $is_resume ) | |
{ | |
//Only send partial content header if downloading a piece of the file (IE workaround) | |
if( !empty( $seek_start ) || $seek_end < ( $size - 1 ) ) | |
header('HTTP/1.1 206 Partial Content' ); | |
else | |
header('HTTP/1.0 200 OK' ); | |
header( 'Accept-Ranges: bytes' ); | |
header( 'Content-Range: bytes ' . $seek_start . '-' . $seek_end . '/' . $size ); | |
$chunk_length = $seek_end - $seek_start + 1; | |
} | |
if( !empty( $_REQUEST['download'] ) ) | |
{ | |
header( "Content-Disposition: attachment; filename=" . urlencode( basename( $file ) ) ); | |
header( "Content-Type: application/download" ); | |
header( "Content-Description: File Transfer" ); | |
header( "Content-Length: " . $chunk_length ); | |
} else { | |
header("Content-Type: " . $mime_type ); | |
header("Content-Length: ". $chunk_length ); | |
} | |
//open the file | |
$fp = fopen( $file, 'rb' ); | |
//seek to start of missing part | |
fseek( $fp, $seek_start ); | |
//start buffered download | |
while( !feof( $fp ) ) | |
{ | |
//reset time limit for big files | |
set_time_limit( 0 ); | |
print( fread( $fp, 1024*8 ) ); | |
flush(); | |
ob_flush(); | |
} | |
fclose( $fp ); | |
exit; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment