Дозакачка с ФТП через скрипт и регулирование скорости скачки.

fixxxer

К.О.
Партнер клуба
Прошу прощения за дезинформацию.. конечно же, не php://stdout, а php://output.
Работающий пример (контроль ошибок и прочее сам сделаешь):

PHP:
<?
$ftpserver = "localhost";
$ftp_folder = "/pub";
$filename = "catalog.tar.gz";


header("Content-type: application/octet-stream");
header("Content-disposition: attachment; filename=\"$filename\"");
$out = fopen("php://output", "ab");
$ftp = ftp_connect($ftpserver);
ftp_login($ftp, "anonymous","[email protected]");
ftp_chdir($ftp, $ftp_folder);
ftp_fget($ftp, $out, $filename, FTP_BINARY);
ftp_close($ftp);
fclose($out);
?>
Так что, тебе осталось только разобраться с эмуляцией докачки по http.
 

Space

Новичок
fixxxer
и как ты предлагаешь засунуть fopen в handle? может я тебя неправильно понял. :)

у меня практически получилось с дозакачкой... только команда REST размер неправильно воспринимает. в чем может быть причина?

PHP:
 $FILE_NAME="test_us.sql.gz";
 $FILE_SIZE  = ... // узнаем через fopen
 if($FILE_SIZE>0)
  {

  $contentLength = $FILE_SIZE;
  $fileOffset = false;
  $fileLength = false;
  if ( isset( $_SERVER['HTTP_RANGE'] ) )
   {
   $httpRange = trim( $_SERVER['HTTP_RANGE'] );
   if ( preg_match( "/^bytes=([0-9]+)-$/", $httpRange, $matches ) )
    {
    $fileOffset = $matches[1];
    header( "Content-Range: bytes $fileOffset-" . $FILE_SIZE - 1 . "/$FILE_SIZE" );
    header( "HTTP/1.1 206 Partial content" );
    $contentLength -= $fileOffset;
    $RESTS=1;
    }
   }
  header("Content-Disposition: attachment; filename=$FILE_NAME");
  header("Accept-Ranges: bytes");
  header("Content-Length: ".$contentLength);
  header("Content-type: application/octet-stream");

function ftp_send($msg)
 {
 global $fp;
 fputs($fp, $msg);
 return fgets ($fp,128);
 }

function get_file($port)
 {
 global $ftp_server;
 $fp = fsockopen ($ftp_server, $port, $errno, $errstr, 3);
 if (!$fp) {
  echo "$errstr ($errno)<br>\n";
  }
 else
  {
  while(!feof($fp))
   {
   echo fgets ($fp,4096);
   flush();
   }
  fclose ($fp);
  }
 }

  $fp = fsockopen ($ftp_server, 21, $errno, $errstr, 30);
  if (!$fp)
   {
   echo "$errstr ($errno)<br>\n";
   }
  else
   {
    fgets ($fp,128);
    ftp_send("USER $user\n");
    ftp_send("PASS $pass\n");
    ftp_send("SYST\n");
    if($RESTS==1)
     {
     $REST_BEGIN=$FILE_SIZE-$contentLength;
     ftp_send("REST $REST_BEGIN\n");
     }
    ftp_send("PWD\n");
    ftp_send("TYPE A\n");
    $pasv = ftp_send("PASV\n");
        $byte = explode(",", $pasv);
        for ($i = 0; $byte[5][$i] != ")"; $i++) {
            $byte2 .= $byte[5][$i];
        }
        $pasv = ($byte[4] * 256) + $byte2;
    fputs($fp, "RETR $FILE_NAME\n");
    get_file($pasv);
 fclose ($fp);
 }
 

Space

Новичок
Блин. не думал что так скоро ответишь. :))) (это по поводу fopen)
 

Space

Новичок
fixxxer
я с твоим вариантом поигрался - чего-то не получается. хотя пробовал и FTP_AUTOSEEK и с FTP_AUTORESUME и указывать позицию начала скачивания. в любом случае качает с начала(хотя в хеадере все как надо - и закачивает столько, сколько надо) :)))

вот пример:

PHP:
......
  header("Accept-Ranges: bytes");
  $contentLength = $FILE_SIZE;
  $fileOffset = false;
  $fileLength = false;
  if ( isset( $_SERVER['HTTP_RANGE'] ) )
   {
   $httpRange = trim( $_SERVER['HTTP_RANGE'] );
   if ( preg_match( "/^bytes=([0-9]+)-$/", $httpRange, $matches ) )
    {
    $fileOffset = $matches[1];
    $ASD_RANGE=$FILE_SIZE-1;
    $ASD= trim($fileOffset."-".$ASD_RANGE);
    $RESUME_POS=$fileOffset;
    header("Content-Range: bytes $ASD/$FILE_SIZE");
    header( "HTTP/1.1 206 Partial content" );
    $contentLength -= $fileOffset;
    $RESTS=1;
    }
   }
   else
   {
    $ASD_RANGE=$FILE_SIZE-1;
    $ASD= trim("0-".$ASD_RANGE);
    $RESUME_POS=0;
    header("Content-Range: bytes ".$ASD."/$FILE_SIZE");
   }
   header("Content-Length: ".$contentLength);
   header("Content-Disposition: attachment; filename=$FILE_NAME");
   header("Content-type: application/octet-stream");
.....
ftp_fget($conn_id, $out, $FILE_NAME, FTP_BINARY, $RESUME_POS);
я все-таки решил закончить свой вариант - все получилось, кроме ОДНОГО - почему то удаленный ФТП-сервер не воспринимает команду REST когда значение больше нуля.
админ того сервака сказал что должно...

хеадер в скрипте такой же и переменный называются так же(см. первый код ЭТОГО сообщения)

fgets ($fp,128);
ftp_send("USER $user\n");
ftp_send("PASS $pass\n");
ftp_send("SYST\n");
ftp_send("REST $RESUME_POS\n");

и вот когда $RESUME_POS>0 сервак не хочет отдавать файл.
 

fixxxer

К.О.
Партнер клуба
Да все просто - сервак не поддерживает докачку.
Потому ни тот, ни другой вариант работать не будет.
Проверь ftp-клиентом.
 

Space

Новичок
т.е.
- в твоем варианте скрипт при перезакачке качает все время с нуля(пока не достигнет размера файла в хедере)
- в моем варианте скрипт может нормально закачать файл если нет перезакачки. если нажал паузу - а потом возобновил - ничего вообще не качает :))))

вот пример моего варианта: http://olgin.ru/soft/file1.php?id=1
 

fixxxer

К.О.
Партнер клуба
хм...
регет какой-то, кажись третий (али четвертый)..вобщем, показывает как он общается с сервером, там окошко с логом...

поставь passive mode, выдери оттуда и попробуй 1 в 1 так же сделать в своем варианте :)
 

Space

Новичок
Я уже пробовал.(имеется ввиду прямое соединение к ФТП-серверу)
первое соединение:
USER *****
PASS *****
SYST
TYPE I
REST 100
REST 0
PWD
CWD /SocksCap220_withKeyG.zip - failed
PASV
LIST -la SocksCap220_withKeyG.zip
PASV
RETR SocksCap220_withKeyG.zip

У меня же первое соединение выглядит:
USER $user
PASS $pass
SYST
REST 100
REST 0
PWD
PASV
RETR $FILE_NAME

===
дозакачка:

USER *****
PASS *****
SYST
TYPE I
REST 100
REST 0
PWD
CWD /SocksCap220_withKeyG.zip - failed
PASV
LIST -la SocksCap220_withKeyG.zip
PASV
REST 24820
RETR SocksCap220_withKeyG.zip

у меня:
USER $user
PASS $pass
SYST
REST 100
REST 0
PWD
PASV
REST $RESUME_POS
RETR $FILE_NAME

т.е., как видишь - только REST подставляется. в принципе обязано работать.

Maxim Matyukhin спрашивал зачем я использую нулевой REST - так в регете такое же :(

пока по диалапу соединялся - сделал как в логах регета. щаз посмотрю резудьтат.
 

fixxxer

К.О.
Партнер клуба
REST 100 а потом REST 0 это проверка, поддерживает ли сервер докачку.
ReGet "просто так" шлет REST 100, смотрит код ответа и запоминает, что ответил сервер (ОК или еррор), потом, дабы "сбросить" 100, шлет REST 0.
У тебя не работает, возможно, потому, что ты не читаешь ответы сервера... Веди уж себя как полноценный ftp-клиент... :)
 

Space

Новичок
вот. кажется заработало со следующими командами:

fgets ($fp,128);
ftp_send("USER $user\n");
ftp_send("PASS $pass\n");
ftp_send("SYST\n");
ftp_send("TYPE I\n");
ftp_send("REST 100\n");
ftp_send("REST 0\n");
ftp_send("PWD\n");
ftp_send("PASV\n");
$pasv = ftp_send("PASV\n");
if($RESUME_POS>0){
ftp_send("REST $RESUME_POS\n");
}
$byte = explode(",", $pasv);
for ($i = 0; $byte[5][$i] != ")"; $i++) {
$byte2 .= $byte[5][$i];
}
$pasv = ($byte[4] * 256) + $byte2;
fputs($fp, "RETR $FILE_NAME\n");
get_file($pasv);
fclose ($fp);
 

fixxxer

К.О.
Партнер клуба
ftp_send("REST 100\n");
ftp_send("REST 0\n");
вот это можешь убрать нафиг и просто
ftp_send("REST $RESUME_POS\n");
 

Space

Новичок
Блин. не успеваю я написать новый ответ.... Ладно щаз все протестирую потом код весь выкину, думаю многим такая заморочка пригодится.
 

Space

Новичок
я сразу глубоко извиняюсь за то, что код не оптимизирован и написан по-корявому. Но! главное - работает - а если работает, то лучше не трогать :))))
PHP:
<?php
// error_reporting(E_ALL);
require("../inc/db.inc.php");

$ftp_server = "r1.chels.ru";
$user="******";
$pass="*******";

$RESTS=0;
function usleepWindows($usec)
{
    $start = gettimeofday();

    do
    {
        $stop = gettimeofday();
        $timePassed = 1000000 * ($stop['sec'] - $start['sec'])
            + $stop['usec'] - $start['usec'];
    }
    while ($timePassed < $usec);
}


$id=(int)$id;

$byte2="";
$REST_BEGIN=0;
$q="select `id` , `patch_name` from `programs` where `id`='".intval(abs($id))."'";
$m=mysql_query($q);
if(mysql_num_rows($m)>0)
 {
 $a=mysql_fetch_array($m);
 $FILE_NAME=$a["patch_name"];
// $FILE_NAME="test.txt";
 $conn_id = ftp_connect($ftp_server);
 $login_result = ftp_login($conn_id, $user, $pass);

 $FILE_SIZE  = ftp_size($conn_id, urldecode($FILE_NAME));
 $FILE_PATCH = "ftp://".$user.":".$pass."@".$ftp_server."/".$FILE_NAME;
 if($FILE_SIZE>0)
  {

  header("Accept-Ranges: bytes");
  $contentLength = $FILE_SIZE;
  $fileOffset = false;
  $fileLength = false;
  if ( isset( $_SERVER['HTTP_RANGE'] ) )
   {
   $httpRange = trim( $_SERVER['HTTP_RANGE'] );
   if ( preg_match( "/^bytes=([0-9]+)-$/", $httpRange, $matches ) )
    {
    $fileOffset = $matches[1];
    $ASD_RANGE=$FILE_SIZE-1;
    $ASD= trim($fileOffset."-".$ASD_RANGE);
    $RESUME_POS=$fileOffset;
    header("Content-Range: bytes $ASD/$FILE_SIZE");
    header( "HTTP/1.1 206 Partial content" );
    $contentLength -= $fileOffset;
    $RESTS=1;
    }
   }
   else
   {
    $ASD_RANGE=$FILE_SIZE-1;
    $ASD= trim("0-".$ASD_RANGE);
    $RESUME_POS=0;
    header("Content-Range: bytes ".$ASD."/$FILE_SIZE");
   }

   header("Content-Length: ".$contentLength);
   header("Content-Disposition: attachment; filename=$FILE_NAME");
   header("Content-type: application/octet-stream");

function ftp_send($msg)
 {
 global $fp;
 fputs($fp, $msg);
 return fgets ($fp,128);
 }
//  print "RESUME_POS[$RESUME_POS]";
function get_file($port)
 {
 global $ftp_server;
 $fp = fsockopen ($ftp_server, $port, $errno, $errstr, 3);
 if (!$fp) {
  echo "$errstr ($errno)<br>\n";
  }
 else
  {
  while(!feof($fp))
   {
//   usleepWindows(200000); /// 1 000 000 - 1 sec
   echo fgets ($fp,4096);
   flush();
   }
  fclose ($fp);
  }
 }

  $fp = fsockopen ($ftp_server, 21, $errno, $errstr, 30);
  if (!$fp)
   {
   echo "$errstr ($errno)<br>\n";
   }
  else
   {
    fgets ($fp,128);
    ftp_send("USER $user\n");
    ftp_send("PASS $pass\n");
    ftp_send("SYST\n");
    ftp_send("TYPE I\n");
    ftp_send("REST 100\n");
    ftp_send("REST 0\n");
    ftp_send("PWD\n");
    ftp_send("PASV\n");
    $pasv = ftp_send("PASV\n");
    if($RESUME_POS>0){
    ftp_send("REST $RESUME_POS\n");
    }
        $byte = explode(",", $pasv);
        for ($i = 0; $byte[5][$i] != ")"; $i++) {
            $byte2 .= $byte[5][$i];
        }
        $pasv = ($byte[4] * 256) + $byte2;
    fputs($fp, "RETR $FILE_NAME\n");
    get_file($pasv);
 fclose ($fp);
 }






   }
 else
  {
  print "
  <pre>
  <b>File not found</b>!
  </pre>
  ";
  }
 }
?>
 

fixxxer

К.О.
Партнер клуба
ftp_send("SYST\n");
ftp_send("REST 100\n");
ftp_send("REST 0\n");
ftp_send("PWD\n");

Вот это можешь безболезненно убрать.
 

Space

Новичок
И еще литтл вопрос.
мне надо точно регулировать скорость закачки и что бы это не влияло на качество загружаемого файла
при использовании функции usleepWindows()- она входит в сам код(см. код выше),
либо при вызывании стандартной usleep(), файл может "побиться"
как избежать "битья" файла?
почему файл бьется(не всегда(!)), если я использую функцию в таком виде:

PHP:
  while(!feof($fp))
   {
   usleep(200000);
   echo fgets ($fp,4096);
   flush();
   }
 
Сверху