2017-10-29 21 views
3

暗黙のsslを使用してftpでphp curlを使用してファイルを取得する際に問題が発生しました(ここで説明されているように:ftp_ssl_connect with implicit ftp over tls)。問題は時にはです - おそらく5%の時間、私は部分的なダウンロードで終了します。PHPを使用したFTPS部分的ダウンロード中のCurl

私のクラスは、多かれ少なかれニコWesterdaleの答えから適応し、ここに関連するメソッドですから構成されている:私は上記のように

$ftps = new ftps('example.com','john_doe','123456'); 
$ftps->download('remote_filename','local_filename'); 

:私はこのようにそれを使用してい

class ftps { 

    private $server; 
    private $username; 
    private $password; 
    private $curlhandle; 
    public $dir = '/'; 

    public function __construct($server, $username, $password) { 
     $this->server = $server; 
     $this->username = $username; 
     $this->password = $password; 
     $this->curlhandle = curl_init(); 
    } 

    private function common($remote) { 
     curl_reset($this->curlhandle); 
     curl_setopt($this->curlhandle, CURLOPT_URL, 'ftps://' . $this->server . '/' . $remote); 
     curl_setopt($this->curlhandle, CURLOPT_USERPWD, $this->username . ':' . $this->password); 
     curl_setopt($this->curlhandle, CURLOPT_SSL_VERIFYPEER, FALSE); 
     curl_setopt($this->curlhandle, CURLOPT_SSL_VERIFYHOST, FALSE); 
     curl_setopt($this->curlhandle, CURLOPT_FTP_SSL, CURLFTPSSL_TRY); 
     curl_setopt($this->curlhandle, CURLOPT_FTPSSLAUTH, CURLFTPAUTH_TLS); 
     return $this->curlhandle; 
    } 

    public function download($filepath, $local = false) { 
     $filename = basename($filepath); 
     $remote = dirname($filepath); 
     if ($remote == '.') { 
      $remote = $this->dir; 
     } 
     if ($local === false) { 
      $local = $filename; 
     } 

     if ($fp = fopen($local, 'w')) { 
      $this->curlhandle = self::common($remote . $filename); 
      curl_setopt($this->curlhandle, CURLOPT_UPLOAD, 0); 
      curl_setopt($this->curlhandle, CURLOPT_FILE, $fp); 
      curl_exec($this->curlhandle); 
      if (curl_error($this->curlhandle)) { 
       return false; 
      } else { 
       return $local; 
      } 
     } 
     return false; 
    } 
} 

これはほぼ完璧に動作します部分的にダウンロードされたファイルの結果の約5%を除いて。次に、リモートサーバーをチェックして、ファイルが実際にそこにあることを確認することができます。スクリプトをもう一度試してください。そして、それは必ず2回目の試みでファイル全体を取得します。

このようにcurlを使用して断続的な問題が発生する原因は何ですか?私の次の動きは、何らかのチェックサムを実装し、すべてのハッシュまでダウンロードの試行を続けることですが、これは本当の解決策よりもむしろ厄介な回避策のように感じ、問題の実際の根本を知ることはうれしいでしょう。

+0

エラーの場合は、 'curl_exec($ this-> curlhandle);の結果を確認してみてください。また、コンテンツサイズとレスポンス時のコンテンツの長さヘッダーをチェックし、不一致がないかどうかを確認してください。次にタイムアウト値も追加します。'CURLOPT_CONNECTIMEOUT'、' CURLOPT_FTP_RESPONSE_TIMEOUT'、 'CURLOPT_ACCEPTTIMEOUT_MS'、' CURLOPT_TIMEOUT'です。最後のものは完全な要求です。残りはリクエストの段階です。だから、これらのタイムアウトで遊んでみて、エラー率が低下するかどうかを見てください。また、あなたのスクリプトが実行するのに十分な時間を得て、実行時にウェブサーバによって殺されていないことを確認してください –

答えて

6

curlと思われるかもしれませんが、curl_error()はおそらくCURLE_PARTIAL_FILEエラーとして報告しますが、あなたのコードはそのエラーを完全に無視しています。代わりに

 if (curl_error($this->curlhandle)) { 
      return false; 
     } else { 

 if (curl_errno($this->curlhandle)) { 
      throw new \RuntimeException('curl error: '.curl_errno($this->curlhandle).': '.curl_error($this->curlhandle)); 
    } else { 

を試すのカールのダウンロードに失敗した場合、今、あなたは適切なエラーを取得する必要があります。しかし、デバッグに何かを与えるために、私は、クラスFTPSに​​を追加し、__constructでお勧めの操作を行います。curl_setopt_array($this->curlhandle,array(CURLOPT_VERBOSE=>true,CURLOPT_STDERR=>($this->curldebugfileh=tmpfile())));

し、その後に、例外を変更します。

  throw new \RuntimeException('curl error: '.curl_errno($this->curlhandle).': '.curl_error($this->curlhandle).' curl verbose log: '.file_get_contents(stream_get_meta_data($this->curldebugfileh)['uri'])); 

と__destructに追加:fclose($this->curldebugfileh);

ここで、壊れたダウンロードまで何が起こったのかの例外に関する冗長なログが表示されるはずです。これはおそらくダウンロードが破損した原因を明らかにするでしょう。

編集:私はあなたが__destructを持っておらず、カールハンドルを決して閉じていないことが分かり、メモリがリークしています。おそらくそれも修正すべきです。 function __destruct(){curl_close($this->curlhandle);fclose($this->curldebugfileh);}を追加すると、メモリ/リソースのリークを防ぐことができます。

編集2:あなたがやっているのはwを使用しないでください。これは、特定のOS(マイクロソフトウィンドウズのような特定のOS) wbを使用して、Windows(OS X以前のMac、DOS、CP/M、OS/2、Symbian、その他のOSを含む)上で実行することは安全です。

edit 3:あなたはfclose($ fp)することがないため、リソースが漏れてしまいます。おそらくそれを修正すべきです。

関連する問題