За последние 24 часа нас посетили 18175 программистов и 1589 роботов. Сейчас ищут 994 программиста ...

Как сделать нормальный download?

Тема в разделе "PHP для новичков", создана пользователем Asmodey, 5 дек 2007.

  1. Asmodey

    Asmodey Активный пользователь

    С нами с:
    5 дек 2007
    Сообщения:
    11
    Симпатии:
    0
    Как на рапиде? Ну или вообще как сделать чтоб не через цикл fread -> print выводить файл как контент страницы, а чтобы работало как при нажатии на обычную ссылку. Ато получается что пока клиент не скачает файл - нельзя перейти по ссылке на другую страницу... Может это как-нить через отдельный поток делается и посылка данных идёт через сокеты?

    Ну или может кто подскажет как сделать вызов скачки по ftp с авторизацией на ftp сервере через вызов download.php?index=xx...? В общем делитесь кусками кода... не стесняйтесь...
     
  2. Psih

    Psih Активный пользователь
    Команда форума Модератор

    С нами с:
    28 дек 2006
    Сообщения:
    2.678
    Симпатии:
    6
    Адрес:
    Рига, Латвия
    Лучше всего использовать скачку напрямую с WEB сервера, а не через PHP.
    Есть модули, которые позволяют скачивать только авторизованным пользователям, на стороне PHP нужно позаботиться только о генерации правильной ссылки. Как пример - модуль для Lighttpd - http://trac.lighttpd.net/trac/wiki/Docs:ModSecDownload. Для apache я думаю есть тоже что-то похожее, ищите сами (я не использую апач вообще)
     
  3. Ti

    Ti Активный пользователь

    С нами с:
    3 июл 2006
    Сообщения:
    2.378
    Симпатии:
    1
    Адрес:
    d1.ru, Екатеринбург
  4. Asmodey

    Asmodey Активный пользователь

    С нами с:
    5 дек 2007
    Сообщения:
    11
    Симпатии:
    0
    Угу, закрыл...заработала.

    Ещё вопросик - при докачке файла (300 Мб) с любого места - сервер начинает бешено юзать виртуальную память и всю её забивает... что это за такое? Может это команда fseek так действует на него? Памяти на сервере маловато конечно - 256 метров...но всёж 700 метров в виртуалку записать - с какого хрена?
     
  5. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    покажи, как читаешь.
     
  6. Asmodey

    Asmodey Активный пользователь

    С нами с:
    5 дек 2007
    Сообщения:
    11
    Симпатии:
    0
    PHP:
    1.  function downloadFile($realFilePath)
    2.  {
    3.      // вначале проверим, что файл существует
    4.      if(!file_exists($realFilePath))
    5.      {
    6.          return false;
    7.      }
    8.      // соберем необходимую информацию о файле
    9.      $CLen = filesize($realFilePath);
    10.      $filename = basename($realFilePath); // запрашиваемое имя
    11.      $file_extension = strtolower(substr(strrchr($filename, '.'), 1));
    12.      $fileCType = mime_content_type($realFilePath);
    13.  
    14.      // Формируем HTTP-заголовки ответа
    15.      // $_SERVER['HTTP_RANGE'] — номер байта, c которого надо возобновить передачу содержимого файла.
    16.      // проверим, что заголовок Range: bytes=range- был послан браузером или менеджером закачек
    17.      if(isset($_SERVER['HTTP_RANGE']))
    18.      {
    19.          $matches = array();
    20.          if(preg_match('/bytes=(\d+)-/', $_SERVER['HTTP_RANGE'], $matches))
    21.          {
    22.              $rangePosition = intval($matches[1]);
    23.              $newCLen = $CLen - $rangePosition;
    24.              header ( 'HTTP/1.1 206 Partial content', true, 200 );
    25.              header ( 'Status: 206 Partial content' );
    26.              // Last-Modified - Дата послднего изменения содержимого. Поле актуально только для
    27.              // статических страниц. Apache заменяет это поле значением поля Date для динамически
    28.              // генерируемых страниц, в том числе для страниц содержащих SSI.
    29.              header ( 'Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');// always modified
    30.              // HTTP/1.1
    31.              // Cache-control: no-cache - Управление кэш. Значение no-cache определяет запрет кэш
    32.              // данной страницы. Для версии протокола HTTP/1.0 действует "Pragma: no-cache".
    33.              header ( 'Cache-Control: no-store, no-cache, must-revalidate ');
    34.              header ( 'Cache-Control: post-check=0, pre-check=0', false);
    35.              // HTTP/1.0
    36.              header ( 'Pragma: no-cache' );
    37.              header ( 'Accept-Ranges: bytes');
    38.              header ( 'Content-Range: bytes ' . $rangePosition . '-' . $CLen - 1 . '/' . $CLen);
    39.              header ( 'Content-Length: ' . $newCLen );
    40.              header ( 'Content-Disposition: attachment; filename="' . $filename . '"' );
    41.              header ( 'Content-Description: File Transfer' );
    42.              header ( 'Content-Type: ' . $fileCType );
    43.              header ( 'Content-Transfer-Encoding: binary');
    44.          }
    45.          else
    46.          {
    47.              return false;
    48.          }
    49.      }
    50.      else
    51.      {
    52.          header ( 'HTTP/1.1 200 OK', true, 200 );
    53.          header ( 'Status: 200 OK' );
    54.          // Last-Modified - Дата послднего изменения содержимого. Поле актуально только для
    55.          // статических страниц. Apache заменяет это поле значением поля Date для динамически
    56.          // генерируемых страниц, в том числе для страниц содержащих SSI.
    57.          header ( 'Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT');// always modified
    58.          // HTTP/1.1
    59.          // Cache-control: no-cache - Управление кэш. Значение no-cache определяет запрет кэш
    60.          // данной страницы. Для версии протокола HTTP/1.0 действует "Pragma: no-cache".
    61.          header ( 'Cache-Control: no-store, no-cache, must-revalidate ');
    62.          header ( 'Cache-Control: post-check=0, pre-check=0', false);
    63.          // HTTP/1.0
    64.          header ( 'Pragma: no-cache' );
    65.          header ( 'Accept-Ranges: bytes');
    66.          header ( 'Content-Length: ' . $CLen );
    67.          header ( 'Content-Disposition: attachment; filename="' . $filename . '"' );
    68.          header ( 'Content-Description: File Transfer' );
    69.          header ( 'Content-Type: ' . $fileCType );
    70.          header ( 'Content-Transfer-Encoding: binary');
    71.          $rangePosition = 0;
    72.      }
    73.      // теперь необходимо встать на позицию $rangePosition и выдать в поток содержимое файла
    74.      $handle = @fopen($realFilePath, 'rb');
    75.      if ($handle)
    76.      {
    77.          fseek($handle, $rangePosition);
    78.          
    79.          while(!feof($handle) and !connection_status())
    80.          {
    81.              print fread($handle, (1024 * 32)); // передача данных по 32 кб за цикл.
    82.          }
    83.          fclose($handle);
    84.          return true;
    85.      }
    86.      else
    87.      {
    88.          return false;
    89.      }
    90.  }
     
  7. +Sten+

    +Sten+ Активный пользователь

    С нами с:
    27 авг 2007
    Сообщения:
    978
    Симпатии:
    0
    Скромно предположу, что проблему может решить fflush($handle) в каждой интерации.
     
  8. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    просто flush, если только.
     
  9. Asmodey

    Asmodey Активный пользователь

    С нами с:
    5 дек 2007
    Сообщения:
    11
    Симпатии:
    0
    да...вроде помогло...сделал так:
    PHP:
    1. $SpeedLimit_KBits = 50; // желаемая скорость передачи (Кбайт/с)
    2. $BlockSize = $SpeedLimit_KBits * 1024;  // размер блока передачи сообразно желаемой скорости передачи (Кбайт/с)
    3.  
    4. // теперь необходимо встать на позицию $rangePosition и выдать в поток содержимое файла
    5. $handle = @fopen($realFilePath, 'rb');
    6. if ($handle)
    7. {
    8.      fseek($handle, $rangePosition); // становимся в нужную позицию файла - для поддержки докачки
    9.      
    10.      while(!feof($handle) and !connection_status())
    11.      {
    12.           print fread($handle, $BlockSize); // собственно передача файла как контента...
    13.           flush ();
    14.           sleep (1); // пауза в 1 сек... обеспечивает желаемое ограничение скорости передачи файла...   
    15.      }
    16.      flush (); // Нужно оно тут ???
    17.      fclose($handle);
    18.      return true;
    19. }
    20. else
    21. {
    22.      return false;
    23. }
    Теперь ещё разобраться бы с последовательностью вывода заголовков...
    В приведённом коде явно что-то не то...а в ReGet ктому же последовательность принятая им не соответствует той что передаётся....
    Есть у кого-нить грамотный кусок кода с выводом заголовков перед передачей?
    Нужно запускать сайт, а там ещё конь не валялся...и времени разбираться как всегда нет... Помогите.
     
  10. antonn

    antonn Активный пользователь

    С нами с:
    10 июн 2007
    Сообщения:
    2.996
    Симпатии:
    0
    а почему бы не взять готовый класс, позволяющий с докачкой работать?
     
  11. Asmodey

    Asmodey Активный пользователь

    С нами с:
    5 дек 2007
    Сообщения:
    11
    Симпатии:
    0
    А где взять? Дайте, я возьму ;o)
     
  12. Asmodey

    Asmodey Активный пользователь

    С нами с:
    5 дек 2007
    Сообщения:
    11
    Симпатии:
    0
    Почему получается запустить только 2 закачки через браузер, а потом сайт замораживается пока не закроешь одну из них?

    в настройках апача:

    MaxKeepAliveRequests 100

    <IfModule mpm_winnt.c>
    ThreadsPerChild 100
    MaxRequestsPerChild 0
    </IfModule>
     
  13. antonn

    antonn Активный пользователь

    С нами с:
    10 июн 2007
    Сообщения:
    2.996
    Симпатии:
    0
    очень буду рад правильным пинкам %)
    PHP:
    1. <?
    2. /*----------------------------------------------------------------------+
    3. | Modified for use with PHP-Fusion 6 by CrappoMan ([email=crappoman@email.com]crappoman@email.com[/email]) |
    4. +-----------------------------------------------------------------------*/
    5.  
    6. /*------------------
    7. | Download Handler |
    8. ------------------*/
    9.  
    10. /*
    11.  @author Nguyen Quoc Bao <quocbao.coder@gmail.com>
    12.  visit [url=http://en.vietapi.com/wiki/index.php/PHP:_HttpDownload]http://en.vietapi.com/wiki/index.php/PHP:_HttpDownload[/url] for class information
    13.  Please send me an email if you find some bug or it doesn't work with download manager.
    14.  I've tested it with
    15.   - Reget : [url=http://reget.com]http://reget.com[/url]
    16.   - FDM : [url=http://freefiledownloadmanager.org]http://freefiledownloadmanager.org[/url]
    17.  @version 1.2
    18.  @desc A simple object for processing download operation , support section downloading
    19.  @distribution It's free as long as you keep this header .
    20.  @sample
    21.  
    22.  1: File Download
    23.   $object = new httpdownload;
    24.   $object->set_byfile($filename); //Download from a file
    25.   $object->use_resume = true; //Enable Resume Mode
    26.   $object->download(); //Download File
    27.  
    28.  2: Data Download
    29.   $object = new httpdownload;
    30.   $object->set_bydata($data); //Download from php data
    31.   $object->use_resume = true; //Enable Resume Mode
    32.   $object->set_filename($filename); //Set download name
    33.   $object->set_mime($mime); //File MIME (Default: application/otect-stream)
    34.   $object->download(); //Download File
    35.  
    36.   3: Manual Download
    37.   $object = new httpdownload;
    38.   $object->set_filename($filename);
    39.   $object->download_ex($size);
    40.   //output your data here , remember to use $this->seek_start and $this->seek_end value :)
    41.  
    42. */
    43.  
    44. class httpdownload {
    45.  
    46.   /*----------------
    47.   | Class Variable |
    48.   ----------------*/
    49.   /**
    50.    $handler : Object Handler
    51.    $use_resume : use section download
    52.    $use_autoexit : auto stop after finishing download
    53.    $use_auth : use authentication download
    54.    $data : Download Data
    55.    $data_len : Download Data Len
    56.    $data_type : Download Data Type
    57.    $data_mod : Last modified time
    58.    $filename : Download File Name
    59.    $mime : File mime
    60.    $bufsize : BUFFER SIZE
    61.    $seek_start : Start Seek
    62.    $seek_end : End Seek
    63.   **/
    64.   var $handler = array('auth' => false ,'header' => false,'fopen'=>false,'fclose'=>false,'fread'=>false,'fseek' => false);
    65.   var $use_resume = true;
    66.   var $use_autoexit = true;
    67.   var $use_auth = false;
    68.   var $data = null;
    69.   var $data_filename = "";
    70.   var $data_len = 0;
    71.  
    72.   var $data_type = 0;
    73.  
    74.   var $data_mod = 0;
    75.   var $filename = null;
    76.   var $mime = null;
    77.   var $bufsize = 2048;
    78.   var $seek_start = 0;
    79.   var $seek_end = -1;
    80.  
    81.   /*-------------------
    82.   | Download Function |
    83.   -------------------*/
    84.   /**
    85.    pre_download() : Pre Download Function
    86.    download() : Download all file
    87.    set_byfile() : Set data download by file
    88.    set_bydata() : Set data download by data
    89.    set_byurl() : Set data download by url
    90.    set_filename() : Set file name
    91.    set_mime() : Set file mime
    92.    download_header() : Send header
    93.    download_ex() : Manual Download
    94.   **/
    95.   function pre_download() {
    96.     global $HTTP_SERVER_VARS;
    97.     if ($this->use_auth) { //use authentication
    98.       if (!$this->_auth()) { //no authentication
    99.         $this->_header('WWW-Authenticate: Basic realm="Please enter your username and password"');
    100.         $this->_header('HTTP/1.0 401 Unauthorized');
    101. //        $this->_header('Status: 401 Unauthorized');
    102.         echo "Unauthorized Access: username or password incorrect\n";
    103.         if ($this->use_autoexit) exit();
    104.         return false;
    105.       }
    106.     }
    107.     if ($this->mime == null) $this->mime = "application/octet-stream"; //default mime
    108.     if (isset($_SERVER['HTTP_RANGE']) || isset($HTTP_SERVER_VARS['HTTP_RANGE'])) {
    109.       if(isset($HTTP_SERVER_VARS['HTTP_RANGE'])) {
    110.         $seek_range = substr($HTTP_SERVER_VARS['HTTP_RANGE'] , strlen('bytes='));
    111.       } else {
    112.         $seek_range = substr($_SERVER['HTTP_RANGE'] , strlen('bytes='));
    113.       }
    114.       $range = explode('-',$seek_range);
    115.       if($range[0] > 0) {
    116.         $this->seek_start = intval($range[0]);
    117.       }
    118.       if($range[1] > 0) {
    119.         $this->seek_end = intval($range[1]);
    120.       } else {
    121.         $this->seek_end = -1;
    122.       }
    123.     } else {
    124.       $this->seek_start = 0;
    125.       $this->seek_end = -1;
    126.     }
    127.     if ($this->seek_start < 0 || !$this->use_resume) $this->seek_start = 0;
    128.     return true;
    129.   }
    130.  
    131.   function download_ex($size) {
    132.     if (!$this->pre_download()) return false;
    133.     ignore_user_abort(true);
    134.     //Use seek end here
    135.     if ($this->seek_start > ($size - 1)) $this->seek_start = 0;
    136.     if ($this->seek_end <= 0) $this->seek_end = $size - 1;
    137.     $this->download_header($size,$seek,$this->seek_end);
    138.     $this->data_mod = time();
    139.     return true;
    140.   }
    141.  
    142.   function download() {
    143.     if (!$this->pre_download()) return false;
    144.     $seek = $this->seek_start;
    145.     ignore_user_abort(true);
    146.     @set_time_limit(0);
    147.     $size = $this->data_len;
    148.     if ($this->data_type == 0) {
    149.       $size = filesize($this->data);
    150.       if ($seek > ($size - 1)) $seek = 0;
    151.       if ($this->filename == null) $this->filename = basename($this->data);
    152.       $res =& $this->_fopen($this->data,'rb');
    153.       if ($seek) $this->_fseek($res , $seek);
    154.       if ($this->seek_end < $seek) $this->seek_end = $size - 1;
    155.       $this->download_header($size,$seek,$this->seek_end); //always use the last seek
    156.       $size = $this->seek_end - $seek + 1;
    157.       while (!connection_aborted() && $size > 0) {
    158.         if ($size < $this->bufsize) echo $this->_fread($res , $size);
    159.         else echo $this->_fread($res , $this->bufsize);
    160.         $size -= $this->bufsize;
    161.       }
    162.       $this->_fclose($res);
    163.     } else if ($this->data_type == 1) {
    164.       if ($seek > ($size - 1)) $seek = 0;
    165.       if ($this->seek_end < $seek) $this->seek_end = $this->data_len - 1;
    166.       $this->data = substr($this->data , $seek , $this->seek_end - $seek + 1);
    167.       if ($this->filename == null) $this->filename = time();
    168.       $size = strlen($this->data);
    169.       $this->download_header($this->data_len,$seek,$this->seek_end);
    170.       while (!connection_aborted() && $size > 0) {
    171.         echo substr($this->data , 0 , $this->bufsize);
    172.         $this->data = substr($this->data , $this->bufsize);
    173.         $size -= $this->bufsize;
    174.       }
    175.     } else if ($this->data_type == 2) {
    176.       //just send a redirect header
    177.       header('Location: ' . $this->data);
    178.     }
    179.     if ($this->use_autoexit) exit();
    180.     return true;
    181.   }
    182.  
    183.   function download_header($size,$seek_start=null,$seek_end=null) {
    184.     $this->_header('Content-type: ' . $this->mime .' name="'.$this->filename.'"');
    185.     $this->_header('Content-Disposition: attachment; filename="'.$this->filename.'"');
    186.     $this->_header('Last-Modified: ' . date('D, d M Y H:i:s \G\M\T' , $this->data_mod));
    187.     if ($seek_start && $this->use_resume) {
    188.       $this->_header("Content-Length: " . ($seek_end - $seek_start + 1));
    189.       $this->_header('Accept-Ranges: bytes');
    190.       $this->_header("HTTP/1.0 206 Partial Content");
    191.       $this->_header("status: 206 Partial Content");
    192.       $this->_header("Content-Range: bytes $seek_start-$seek_end/$size");
    193.     } else {
    194.       $this->_header("Content-Length: $size");
    195.     }
    196.   }
    197.  
    198.   function set_byfile($dir,$filename) {
    199.     if (is_readable($dir) && is_file($dir)) {
    200.       $this->data_len = 0;
    201.       $this->data = $dir;
    202.       $this->data_filename = $filename;
    203.       $this->data_type = 0;
    204.       $this->data_mod = filemtime($dir);
    205.       return true;
    206.     } else return false;
    207.   }
    208.  
    209.   function set_bydata($data) {
    210.     if ($data == '') return false;
    211.     $this->data = $data;
    212.     $this->data_filename = $data;
    213.     $this->data_len = strlen($data);
    214.     $this->data_type = 1;
    215.     $this->data_mod = time();
    216.     return true;
    217.   }
    218.  
    219.   function set_byurl($data) {
    220.     $this->data = $data;
    221.     $this->data_filename = $data;
    222.     $this->data_len = 0;
    223.     $this->data_type = 2;
    224.     return true;
    225.   }
    226.  
    227.   function set_filename($filename) {
    228.     $this->filename = $filename;
    229.   }
    230.   function set_mime($mime) {
    231.     $this->mime = $mime;
    232.   }
    233.   function set_lastmodtime($time) {
    234.     $time = intval($time);
    235.     if ($time <= 0) $time = time();
    236.     $this->data_mod = $time;
    237.   }
    238.  
    239.   /*----------------
    240.   | Other Function |
    241.   ----------------*/
    242.   /**
    243.    header() : Send HTTP Header
    244.   **/
    245.   function _header($var) {
    246.     if ($this->handler['header']) return @call_user_func($this->handler['header'],$var);
    247.     else return header($var);
    248.   }
    249.  
    250.   function &_fopen($file,$mode) {
    251.     if ($this->handler['fopen']) return @call_user_func($this->handler['fopen'],$file,$mode);
    252.     else return fopen($file,$mode);
    253.   }
    254.  
    255.   function _fclose($res) {
    256.     if ($this->handler['fclose']) return @call_user_func($this->handler['fclose'],$res);
    257.     else return fclose($res);
    258.   }
    259.  
    260.   function _fseek($res,$len) {
    261.     if ($this->handler['fseek']) return @call_user_func($this->handler['fseek'],$res,$len);
    262.     else return fseek($res,$len);
    263.   }
    264.  
    265.   function &_fread($file,$size) {
    266.     if ($this->handler['fread']) return @call_user_func($this->handler['fread'],$file,$size);
    267.     else return fread($file,$size);
    268.   }
    269.  
    270.   function _auth() {
    271.     if (!isset($_SERVER['PHP_AUTH_USER'])) return false;
    272.     if ($this->handler['auth']) return @call_user_func($this->handler['auth'],$_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
    273.     else return true; //you must use a handler
    274.   }
    275.  
    276. }
    277.  
    278. ?>
     
  14. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    21 век уже =)
     
  15. antonn

    antonn Активный пользователь

    С нами с:
    10 июн 2007
    Сообщения:
    2.996
    Симпатии:
    0
    угу :) но это никогда не уберут (в разумных интервалах), чтобы оставить совместимость :)
    но по остальному - скрипт, как мне показалось, довольно удобен, докачка есть :)
     
  16. Sergey89

    Sergey89 Активный пользователь

    С нами с:
    4 янв 2007
    Сообщения:
    4.796
    Симпатии:
    0
    В PHP 6 этого не будет :)
     
  17. antonn

    antonn Активный пользователь

    С нами с:
    10 июн 2007
    Сообщения:
    2.996
    Симпатии:
    0
    да и пофиг %)