Как на рапиде? Ну или вообще как сделать чтоб не через цикл fread -> print выводить файл как контент страницы, а чтобы работало как при нажатии на обычную ссылку. Ато получается что пока клиент не скачает файл - нельзя перейти по ссылке на другую страницу... Может это как-нить через отдельный поток делается и посылка данных идёт через сокеты? Ну или может кто подскажет как сделать вызов скачки по ftp с авторизацией на ftp сервере через вызов download.php?index=xx...? В общем делитесь кусками кода... не стесняйтесь...
Лучше всего использовать скачку напрямую с WEB сервера, а не через PHP. Есть модули, которые позволяют скачивать только авторизованным пользователям, на стороне PHP нужно позаботиться только о генерации правильной ссылки. Как пример - модуль для Lighttpd - http://trac.lighttpd.net/trac/wiki/Docs:ModSecDownload. Для apache я думаю есть тоже что-то похожее, ищите сами (я не использую апач вообще)
Угу, закрыл...заработала. Ещё вопросик - при докачке файла (300 Мб) с любого места - сервер начинает бешено юзать виртуальную память и всю её забивает... что это за такое? Может это команда fseek так действует на него? Памяти на сервере маловато конечно - 256 метров...но всёж 700 метров в виртуалку записать - с какого хрена?
PHP: function downloadFile($realFilePath) { // вначале проверим, что файл существует if(!file_exists($realFilePath)) { return false; } // соберем необходимую информацию о файле $CLen = filesize($realFilePath); $filename = basename($realFilePath); // запрашиваемое имя $file_extension = strtolower(substr(strrchr($filename, '.'), 1)); $fileCType = mime_content_type($realFilePath); // Формируем HTTP-заголовки ответа // $_SERVER['HTTP_RANGE'] — номер байта, c которого надо возобновить передачу содержимого файла. // проверим, что заголовок Range: bytes=range- был послан браузером или менеджером закачек if(isset($_SERVER['HTTP_RANGE'])) { $matches = array(); if(preg_match('/bytes=(\d+)-/', $_SERVER['HTTP_RANGE'], $matches)) { $rangePosition = intval($matches[1]); $newCLen = $CLen - $rangePosition; header ( 'HTTP/1.1 206 Partial content', true, 200 ); header ( 'Status: 206 Partial content' ); // Last-Modified - Дата послднего изменения содержимого. Поле актуально только для // статических страниц. Apache заменяет это поле значением поля Date для динамически // генерируемых страниц, в том числе для страниц содержащих SSI. header ( 'Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');// always modified // HTTP/1.1 // Cache-control: no-cache - Управление кэш. Значение no-cache определяет запрет кэш // данной страницы. Для версии протокола HTTP/1.0 действует "Pragma: no-cache". header ( 'Cache-Control: no-store, no-cache, must-revalidate '); header ( 'Cache-Control: post-check=0, pre-check=0', false); // HTTP/1.0 header ( 'Pragma: no-cache' ); header ( 'Accept-Ranges: bytes'); header ( 'Content-Range: bytes ' . $rangePosition . '-' . $CLen - 1 . '/' . $CLen); header ( 'Content-Length: ' . $newCLen ); header ( 'Content-Disposition: attachment; filename="' . $filename . '"' ); header ( 'Content-Description: File Transfer' ); header ( 'Content-Type: ' . $fileCType ); header ( 'Content-Transfer-Encoding: binary'); } else { return false; } } else { header ( 'HTTP/1.1 200 OK', true, 200 ); header ( 'Status: 200 OK' ); // Last-Modified - Дата послднего изменения содержимого. Поле актуально только для // статических страниц. Apache заменяет это поле значением поля Date для динамически // генерируемых страниц, в том числе для страниц содержащих SSI. header ( 'Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');// always modified // HTTP/1.1 // Cache-control: no-cache - Управление кэш. Значение no-cache определяет запрет кэш // данной страницы. Для версии протокола HTTP/1.0 действует "Pragma: no-cache". header ( 'Cache-Control: no-store, no-cache, must-revalidate '); header ( 'Cache-Control: post-check=0, pre-check=0', false); // HTTP/1.0 header ( 'Pragma: no-cache' ); header ( 'Accept-Ranges: bytes'); header ( 'Content-Length: ' . $CLen ); header ( 'Content-Disposition: attachment; filename="' . $filename . '"' ); header ( 'Content-Description: File Transfer' ); header ( 'Content-Type: ' . $fileCType ); header ( 'Content-Transfer-Encoding: binary'); $rangePosition = 0; } // теперь необходимо встать на позицию $rangePosition и выдать в поток содержимое файла $handle = @fopen($realFilePath, 'rb'); if ($handle) { fseek($handle, $rangePosition); while(!feof($handle) and !connection_status()) { print fread($handle, (1024 * 32)); // передача данных по 32 кб за цикл. } fclose($handle); return true; } else { return false; } }
да...вроде помогло...сделал так: PHP: $SpeedLimit_KBits = 50; // желаемая скорость передачи (Кбайт/с) $BlockSize = $SpeedLimit_KBits * 1024; // размер блока передачи сообразно желаемой скорости передачи (Кбайт/с) // теперь необходимо встать на позицию $rangePosition и выдать в поток содержимое файла $handle = @fopen($realFilePath, 'rb'); if ($handle) { fseek($handle, $rangePosition); // становимся в нужную позицию файла - для поддержки докачки while(!feof($handle) and !connection_status()) { print fread($handle, $BlockSize); // собственно передача файла как контента... flush (); sleep (1); // пауза в 1 сек... обеспечивает желаемое ограничение скорости передачи файла... } flush (); // Нужно оно тут ??? fclose($handle); return true; } else { return false; } Теперь ещё разобраться бы с последовательностью вывода заголовков... В приведённом коде явно что-то не то...а в ReGet ктому же последовательность принятая им не соответствует той что передаётся.... Есть у кого-нить грамотный кусок кода с выводом заголовков перед передачей? Нужно запускать сайт, а там ещё конь не валялся...и времени разбираться как всегда нет... Помогите.
Почему получается запустить только 2 закачки через браузер, а потом сайт замораживается пока не закроешь одну из них? в настройках апача: MaxKeepAliveRequests 100 <IfModule mpm_winnt.c> ThreadsPerChild 100 MaxRequestsPerChild 0 </IfModule>
очень буду рад правильным пинкам %) PHP: <? /*----------------------------------------------------------------------+ | Modified for use with PHP-Fusion 6 by CrappoMan ([email=crappoman@email.com]crappoman@email.com[/email]) | +-----------------------------------------------------------------------*/ /*------------------ | Download Handler | ------------------*/ /* @author Nguyen Quoc Bao <quocbao.coder@gmail.com> visit [url=http://en.vietapi.com/wiki/index.php/PHP:_HttpDownload]http://en.vietapi.com/wiki/index.php/PHP:_HttpDownload[/url] for class information Please send me an email if you find some bug or it doesn't work with download manager. I've tested it with - Reget : [url=http://reget.com]http://reget.com[/url] - FDM : [url=http://freefiledownloadmanager.org]http://freefiledownloadmanager.org[/url] @version 1.2 @desc A simple object for processing download operation , support section downloading @distribution It's free as long as you keep this header . @sample 1: File Download $object = new httpdownload; $object->set_byfile($filename); //Download from a file $object->use_resume = true; //Enable Resume Mode $object->download(); //Download File 2: Data Download $object = new httpdownload; $object->set_bydata($data); //Download from php data $object->use_resume = true; //Enable Resume Mode $object->set_filename($filename); //Set download name $object->set_mime($mime); //File MIME (Default: application/otect-stream) $object->download(); //Download File 3: Manual Download $object = new httpdownload; $object->set_filename($filename); $object->download_ex($size); //output your data here , remember to use $this->seek_start and $this->seek_end value :) */ class httpdownload { /*---------------- | Class Variable | ----------------*/ /** $handler : Object Handler $use_resume : use section download $use_autoexit : auto stop after finishing download $use_auth : use authentication download $data : Download Data $data_len : Download Data Len $data_type : Download Data Type $data_mod : Last modified time $filename : Download File Name $mime : File mime $bufsize : BUFFER SIZE $seek_start : Start Seek $seek_end : End Seek **/ var $handler = array('auth' => false ,'header' => false,'fopen'=>false,'fclose'=>false,'fread'=>false,'fseek' => false); var $use_resume = true; var $use_autoexit = true; var $use_auth = false; var $data = null; var $data_filename = ""; var $data_len = 0; var $data_type = 0; var $data_mod = 0; var $filename = null; var $mime = null; var $bufsize = 2048; var $seek_start = 0; var $seek_end = -1; /*------------------- | Download Function | -------------------*/ /** pre_download() : Pre Download Function download() : Download all file set_byfile() : Set data download by file set_bydata() : Set data download by data set_byurl() : Set data download by url set_filename() : Set file name set_mime() : Set file mime download_header() : Send header download_ex() : Manual Download **/ function pre_download() { global $HTTP_SERVER_VARS; if ($this->use_auth) { //use authentication if (!$this->_auth()) { //no authentication $this->_header('WWW-Authenticate: Basic realm="Please enter your username and password"'); $this->_header('HTTP/1.0 401 Unauthorized'); // $this->_header('Status: 401 Unauthorized'); echo "Unauthorized Access: username or password incorrect\n"; if ($this->use_autoexit) exit(); return false; } } if ($this->mime == null) $this->mime = "application/octet-stream"; //default mime if (isset($_SERVER['HTTP_RANGE']) || isset($HTTP_SERVER_VARS['HTTP_RANGE'])) { if(isset($HTTP_SERVER_VARS['HTTP_RANGE'])) { $seek_range = substr($HTTP_SERVER_VARS['HTTP_RANGE'] , strlen('bytes=')); } else { $seek_range = substr($_SERVER['HTTP_RANGE'] , strlen('bytes=')); } $range = explode('-',$seek_range); if($range[0] > 0) { $this->seek_start = intval($range[0]); } if($range[1] > 0) { $this->seek_end = intval($range[1]); } else { $this->seek_end = -1; } } else { $this->seek_start = 0; $this->seek_end = -1; } if ($this->seek_start < 0 || !$this->use_resume) $this->seek_start = 0; return true; } function download_ex($size) { if (!$this->pre_download()) return false; ignore_user_abort(true); //Use seek end here if ($this->seek_start > ($size - 1)) $this->seek_start = 0; if ($this->seek_end <= 0) $this->seek_end = $size - 1; $this->download_header($size,$seek,$this->seek_end); $this->data_mod = time(); return true; } function download() { if (!$this->pre_download()) return false; $seek = $this->seek_start; ignore_user_abort(true); @set_time_limit(0); $size = $this->data_len; if ($this->data_type == 0) { $size = filesize($this->data); if ($seek > ($size - 1)) $seek = 0; if ($this->filename == null) $this->filename = basename($this->data); $res =& $this->_fopen($this->data,'rb'); if ($seek) $this->_fseek($res , $seek); if ($this->seek_end < $seek) $this->seek_end = $size - 1; $this->download_header($size,$seek,$this->seek_end); //always use the last seek $size = $this->seek_end - $seek + 1; while (!connection_aborted() && $size > 0) { if ($size < $this->bufsize) echo $this->_fread($res , $size); else echo $this->_fread($res , $this->bufsize); $size -= $this->bufsize; } $this->_fclose($res); } else if ($this->data_type == 1) { if ($seek > ($size - 1)) $seek = 0; if ($this->seek_end < $seek) $this->seek_end = $this->data_len - 1; $this->data = substr($this->data , $seek , $this->seek_end - $seek + 1); if ($this->filename == null) $this->filename = time(); $size = strlen($this->data); $this->download_header($this->data_len,$seek,$this->seek_end); while (!connection_aborted() && $size > 0) { echo substr($this->data , 0 , $this->bufsize); $this->data = substr($this->data , $this->bufsize); $size -= $this->bufsize; } } else if ($this->data_type == 2) { //just send a redirect header header('Location: ' . $this->data); } if ($this->use_autoexit) exit(); return true; } function download_header($size,$seek_start=null,$seek_end=null) { $this->_header('Content-type: ' . $this->mime .' name="'.$this->filename.'"'); $this->_header('Content-Disposition: attachment; filename="'.$this->filename.'"'); $this->_header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T' , $this->data_mod)); if ($seek_start && $this->use_resume) { $this->_header("Content-Length: " . ($seek_end - $seek_start + 1)); $this->_header('Accept-Ranges: bytes'); $this->_header("HTTP/1.0 206 Partial Content"); $this->_header("status: 206 Partial Content"); $this->_header("Content-Range: bytes $seek_start-$seek_end/$size"); } else { $this->_header("Content-Length: $size"); } } function set_byfile($dir,$filename) { if (is_readable($dir) && is_file($dir)) { $this->data_len = 0; $this->data = $dir; $this->data_filename = $filename; $this->data_type = 0; $this->data_mod = filemtime($dir); return true; } else return false; } function set_bydata($data) { if ($data == '') return false; $this->data = $data; $this->data_filename = $data; $this->data_len = strlen($data); $this->data_type = 1; $this->data_mod = time(); return true; } function set_byurl($data) { $this->data = $data; $this->data_filename = $data; $this->data_len = 0; $this->data_type = 2; return true; } function set_filename($filename) { $this->filename = $filename; } function set_mime($mime) { $this->mime = $mime; } function set_lastmodtime($time) { $time = intval($time); if ($time <= 0) $time = time(); $this->data_mod = $time; } /*---------------- | Other Function | ----------------*/ /** header() : Send HTTP Header **/ function _header($var) { if ($this->handler['header']) return @call_user_func($this->handler['header'],$var); else return header($var); } function &_fopen($file,$mode) { if ($this->handler['fopen']) return @call_user_func($this->handler['fopen'],$file,$mode); else return fopen($file,$mode); } function _fclose($res) { if ($this->handler['fclose']) return @call_user_func($this->handler['fclose'],$res); else return fclose($res); } function _fseek($res,$len) { if ($this->handler['fseek']) return @call_user_func($this->handler['fseek'],$res,$len); else return fseek($res,$len); } function &_fread($file,$size) { if ($this->handler['fread']) return @call_user_func($this->handler['fread'],$file,$size); else return fread($file,$size); } function _auth() { if (!isset($_SERVER['PHP_AUTH_USER'])) return false; if ($this->handler['auth']) return @call_user_func($this->handler['auth'],$_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']); else return true; //you must use a handler } } ?>
угу но это никогда не уберут (в разумных интервалах), чтобы оставить совместимость но по остальному - скрипт, как мне показалось, довольно удобен, докачка есть