$v){ if($extension == $k) $mime = $v; else $mime = 'application/octet-stream'; } $filename = $row['path'] . "/". $row['title'] . "." . $row['type']; //проверка на существование и воспроизведение нужного файла if (!file_exists($filename) && !is_readable($filename)) exit('File does not exist or don`t readable'); //сервер сообщает клиенту, что непротив обслуживать байт-диапазонные запросы header('Accept-Ranges: bytes'); $f = fopen($filename, 'rb'); //проверка, открылся ли файл if(!$f){ header ("HTTP/1.1 505 Internal server error"); exit(); } flock($f, LOCK_EX); fseek($f, 0, SEEK_END); //указатель - в конец файла, получаем позицию указателя $size = ftell($f); //метка объекта, хеш всего файла, md5_file($filename) //выбираем из базы, вычисляем при загрузке данных про файл в базу $etag_server = $row['hash']; //время последней модификации файла $last_mod = filemtime($filename); //клиент запросил докачку if (isset($_SERVER['HTTP_RANGE'])) { //проверяем, что лежит в $_SERVER['HTTP_RANGE']: bytes=20-50/длина файла if (!preg_match('/^bytes=(?:\d+-\d+|-\d+|\d+-//\d+)$/', $_SERVER['HTTP_RANGE'])){ //если range некорректный, посылаем весь текст header("HTTP/1.1 416 Requested Range Not Satisfiable"); header("HTTP/1.1 200 OK"); header('Content-Type: '. $mime); header('Content-Disposition: attachment; filename=' . basename($filename)); header('Content-Length:' . $size); header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); } // условные запросы if(isset($_SERVER['HTTP_IF_RANGE'])){ //определяем, чем являются первые символы - меткой или датой $ifr = trim($_SERVER['HTTP_IF_RANGE']); $iftag = $ifdate = ''; if(ctype_digit($ifr)){ $ifdate = strtoftime($ifr); //если это дата, проверяем время модификации if(isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])){ $ifmod = strtotime($_SERVER['HTTP_IF_MODIFIED_SINCE']); if($ifdate == $ifmod && $ifmod == $last_mod){ header("HTTP/1.1 304 Not Modified"); //файл не поменялся header("ETag: \"". $etag_server ."\""); // последнее изменение header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); //страница не будет меняться 10 минут header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); exit(); }else{ if($ifmod > $last_mod){ //файл изменился, присваиваем метку header("ETag: \"". $etag_server ."\""); // последнее изменение header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); //страница не будет меняться 10 минут header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); } } } }else{ $iftag = $ifr; //если это метка, проверяем, совпадает ли метка объекта с текущей if(isset($_SERVER['HTTP_IF_MATCH']) && $_SERVER['HTTP_IF_MATCH'] != "*"){ $etag_client = trim($_SERVER['HTTP_IF_MATCH']); //метка не совпала if($iftag == $etag_client && $etag_client !== $etag_server){ //присылаем полный объект header("HTTP/1.1 200 OK"); header('Content-Length:' . $size); //надо присвоить новую метку header("ETag: \"". $etag_server ."\""); // зафиксировать последнее изменение header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); //страница не будет меняться 10 минут header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); }else{ //метка совпала, значит, файл не менялся header("HTTP/1.1 304 Not Modified"); header("ETag: \"". $etag_server ."\""); // последнее изменение header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); //страница не будет меняться 10 минут header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); } } }//if(ctype_digit }else{ //условных запросов нет, заголовок - предварит. усл. не выполнены header("HTTP/1.1 412 Precondition Failed"); header("ETag: \"". $etag_server ."\""); // последнее изменение header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); //страница не будет меняться 10 минут header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); exit(); } //определяем диапазон $range=substr($_SERVER['HTTP_RANGE'], strpos($_SERVER['HTTP_RANGE'], '=')+1); $range = substr($range, strpos($range,0,"/")); $range = explode("-", $range); $from = $range[0]; $to = $range[1]; //если первая позиция не задана, берем ее с нуля if($from < 0) $from = 0; if($to) $to = ++ $to; //если не задана позиция конца else $to = $size; //от позиции и до конца, диапазон типа 521000- if($range[1]==''){ $from = $range[0]; $to = $size; } //последние байты, диапазон типа -300000 if($range[0]==''){ $from = $range[1]; $to = $size; } //узнаем, запросил клиент полный файл либо кусок if ($from > 0 || $to < $size) { //первый и последний байт куска $start = $from; $end = $to - 1; header('HTTP/1.1 206 Partial Content'); //длина диапазона, который считывается $part = $to - $from; header('Content-Length:' . $part); header('Content-Range: bytes ' . $start . "'-'" . $end . "'/'" . $size); header("ETag: \"". $etag_server ."\""); // последнее изменение header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); //страница не будет меняться 10 минут header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); }else{ // если клиент не запросил докачку или byte-range-spec некорректна header("HTTP/1.1 200 OK"); header('Content-Length:' . $size); header("ETag: \"". $etag_server ."\""); // последнее изменение header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); //страница не будет меняться 10 минут header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); } }else{ //диапазон неопределен, значит, клиент запросил весь файл header("HTTP/1.1 200 OK"); header('Content-Length:' . $size); header("ETag: \"". $etag_server ."\""); // последнее изменение header ("Last-Modified: " . gmdate("D, d M Y H:i:s \G\M\T", $last_mod)); //страница не будет меняться 10 минут header("Expires: " . gmdate("D, d M Y H:i:s \G\M\T", time()+60*10)); $range = array(0,$size); $from = $range[0]; $to = $range[1]; $start = $from; } //отправляем заголовки и для целого файла, и для частичной загрузки header('Content-Type: '. $mime); header('Content-Disposition: attachment; filename=' . basename($filename)); //ставим указатель на начальную позицию, которую задал клиент fseek($f, $start, SEEK_SET); //выдача файла while(!feof($f)) { if($to == $size){ //выводим остаток до конца файла fpassthru($f); }else{ //выводим начало и середину $read = fread($f, 256000); echo $read; flush(); } } flock($f, LOCK_UN); fclose($f); }//while($row = //счетчик скачиваний $sql_update = "UPDATE books SET count_download = count_download + 1 WHERE id =" . $_GET['id_book']; mysql_query($sql_update); }//if(isset($_GET ?>