Content-Range 헤더는 파일 다운로드 이어받기 할 때 필요한 부분입니다. 동시에 $_SERVER['HTTP_RANGE'] 변수를 같이 사용하고 이로서 이어받기가 가능해집니다. 헤더부분은 Content-Range: bytes 다운받은 크기/전체크기 로 표현합니다. 다음을 참고하면 이해되리라 봅니다. (참고 문서)
Actual result:
--------------
$ telnet dev.conduit-it.com 80
Trying 10.42.84.2...
Connected to dev.conduit-it.com.
Escape character is '^]'.
GET /test.php HTTP/1.1
Host:dev.conduit-it.com
Range:bytes=0-24
Connection:close
HTTP/1.1 206 Partial Content
Date: Fri, 29 Aug 2008 03:43:20 GMT
Content-Range: bytes 0-24/8000
Content-Length: 25
Connection: close
Content-Type: text/html
.........................Connection closed by foreign host.
$ telnet dev.conduit-it.com 80
Trying 10.42.84.2...
Connected to dev.conduit-it.com.
Escape character is '^]'.
GET /test.php HTTP/1.1
Host:dev.conduit-it.com
Range:bytes=0-24,50-74
Connection:close
HTTP/1.1 206 Partial Content
Date: Fri, 29 Aug 2008 03:45:44 GMT
Content-Length: 240
Connection: close
Content-Type: multipart/byteranges; boundary=455911696d6f354a2
--455911696d6f354a2
Content-type: text/html
Content-range: bytes 0-24/8000
.........................
--455911696d6f354a2
Content-type: text/html
Content-range: bytes 50-74/8000
.........................
--455911696d6f354a2--
Connection closed by foreign host.
헤더 부분은 다음과 같이 표현됩니다.
Accept-Ranges: bytes
Content-Length: {filesize}
Content-Range: bytes 10-{filesize-1}/{filesize}
이러한 자료를 토대로 파일 이어받기는 다음과 같이 구현할 수 있겠습니다.
<?php
function download_file($file_name) {
if (!file_exists($file_name)) { die("<b>404 File not found!</b>"); }
$file_extension = strtolower(substr(strrchr($file_name,"."),1));
$file_size = filesize($file_name);
$md5_sum = md5_file($file_name);
switch($file_extension) {
case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "mp3": $ctype="audio/mpeg"; break;
case "mpg":$ctype="video/mpeg"; break;
case "avi": $ctype="video/x-msvideo"; break;
case "php":
case "htm":
case "html":
case "txt":
die("<b>Cannot be used for ". $file_extension . " files!</b>");
break;
default: $ctype="application/force-download";
}
if (isset($_SERVER['HTTP_RANGE'])) {
$partial_content = true;
$range = explode("-", $_SERVER['HTTP_RANGE']);
$offset = intval($range[0]);
$length = intval($range[1]) - $offset;
}
else {
$partial_content = false;
$offset = 0;
$length = $file_size;
}
$handle = fopen($file_name, 'r');
$buffer = '';
fseek($handle, $offset);
$buffer = fread($handle, $length);
$md5_sum = md5($buffer);
if ($partial_content)
$data_size = intval($range[1]) - intval($range[0]);
else
$data_size = $file_size;
fclose($handle);
header("Content-Length: " . $data_size);
header("Content-md5: " . $md5_sum);
header("Accept-Ranges: bytes");
if ($partial_content)
header('Content-Range: bytes ' . $offset . '-' . ($offset + $length) . '/' . $file_size);
header("Connection: close");
header("Content-type: " . $ctype);
header('Content-Disposition: attachment; filename=' . $file_name);
echo $buffer;
flush();
}
?>
출처: http://php.net/manual/en/function.fread.php
<?php
function dl_file_resume($file){
if (!is_file($file)) { die("<b>404 File not found!</b>"); }
$len = filesize($file);
$filename = basename($file);
$file_extension = strtolower(substr(strrchr($filename,"."),1));
switch( $file_extension ) {
case "exe": $ctype="application/octet-stream"; break;
case "zip": $ctype="application/zip"; break;
case "mp3": $ctype="audio/mpeg"; break;
case "mpg":$ctype="video/mpeg"; break;
case "avi": $ctype="video/x-msvideo"; break;
case "php":
case "htm":
case "html":
case "txt": die("<b>Cannot be used for ". $file_extension ." files!</b>"); break;
default: $ctype="application/force-download";
}
header("Pragma: public");
header("Expires: 0");
header("Cache-Control:");
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Type: $ctype");
$filespaces = str_replace("_", " ", $filename);
$header='Content-Disposition: attachment; filename='.$filespaces.';';
header($header );
header("Content-Transfer-Encoding: binary");
$size=filesize($file);
if(isset($_ENV['HTTP_RANGE'])) {
list($a, $range)=explode("=",$_ENV['HTTP_RANGE']);
str_replace($range, "-", $range);
$size2=$size-1;
header("Content-Range: $range$size2/$size");
$new_length=$size2-$range;
header("Content-Length: $new_length");
} else {
$size2=$size-1;
header("Content-Range: bytes 0-$size2/$size");
header("Content-Length: ".$size2);
}
$fp=fopen("$file","r");
fseek($fp,$range);
while(!feof($fp)) {
set_time_limit();
print(fread($fp,1024*8));
flush();
}
fclose($fp);
exit;
}
?>
출처: http://nc.php.net/manual/vote-note.php?id=50904&page=function.fread&vote=up
<?php
function smartReadFile($location, $filename, $mimeType='application/octet-stream'){
if(!file_exists($location))
{
header ("HTTP/1.0 404 Not Found");
return;
}
$size=filesize($location);
$time=date('r',filemtime($location));
$fm=@fopen($location,'rb');
if(!$fm)
{
header ("HTTP/1.0 505 Internal server error");
return;
}
$begin=0;
$end=$size;
if(isset($_SERVER['HTTP_RANGE']))
{
if(preg_match('/bytes=\h*(\d+)-(\d*)[\D.*]?/i', $_SERVER['HTTP_RANGE'], $matches))
{
$begin=intval($matches[0]);
if(!empty($matches[1]))
$end=intval($matches[1]);
}
}
if($begin>0||$end<$size)
header("HTTP/1.0 206 Partial Content");
else
header('HTTP/1.0 200 OK');
header("Content-Type: $mimeType");
header('Cache-Control: public, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('Accept-Ranges: bytes');
header('Content-Length:'.($end-$begin));
header("Content-Range: bytes $begin-$end/$size");
header("Content-Disposition: inline; filename=$filename");
header("Content-Transfer-Encoding: binary\n");
header("Last-Modified: $time");
header('Connection: close');
$cur=$begin;
fseek($fm,$begin,0);
while(!feof($fm)&&$cur<$end&&(connection_status()==0))
{ print fread($fm,min(1024*16,$end-$cur));
$cur+=1024*16;
}
}
?>
출처: http://php.net/manual/ja/function.readfile.php