2017-10-29 6 views
3

Ich habe ein Problem beim Abrufen einer Datei mit PHP curl über ftps mit impliziten ssl (wie hier diskutiert: ftp_ssl_connect with implicit ftp over tls). Das Problem ist, dass manchmal - wahrscheinlich 5% der Zeit, am Ende mit einem partiellen Download.FTPS mit PHP Curl bekommen partiellen Download

Meine Klasse ist mehr oder weniger angepasst von angepasst von Nico Westerdale Antwort und hier sind die relevanten Methoden:

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; 
    } 
} 

ich es so bin mit:

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

Wie ich bereits erwähnt funktioniert das fast fehlerfrei außer etwa 5% der Zeit ist das Ergebnis eine teilweise heruntergeladene Datei. Ich überprüfe dann den Remote-Server und bin in der Lage zu überprüfen, ob die Datei tatsächlich in ihrer Gesamtheit vorhanden ist - probiere das Skript erneut aus und es erhält unweigerlich die gesamte Datei bei einem zweiten Versuch.

Was würde ein intermittierendes Problem bei der Verwendung von Curl wie dies verursachen? Mein nächster Schritt wäre, eine Art Prüfsummen zu implementieren und Download-Versuche fortzusetzen, bis alles hashe, aber das fühlt sich eher wie eine schlampige Problemumgehung als eine echte Lösung an und es wäre schön, die eigentliche Wurzel des Problems zu kennen.

+0

Überprüfen Sie die Ergebnisse von 'curl_exec ($ this-> lurlhandle);' im Falle von Fehlern. Überprüfen Sie auch die Inhaltsgröße und den Header der Inhaltslänge bei der Antwort, um festzustellen, ob eine Nichtübereinstimmung vorliegt. Als nächstes werde ich auch Timeout-Werte hinzufügen.'CURLOPT_CONNECTIMEOUT',' CURLOPT_FTP_RESPONSE_TIMEOUT', 'CURLOPT_ACCEPTTIMEOUT_MS',' CURLOPT_TIMEOUT'. Der letzte ist für eine vollständige Anfrage. Rest sind für Phasen der Anfrage. Versuchen Sie also, mit diesen Timeouts herumzuspielen und zu sehen, ob die Fehlerrate sinkt. Stellen Sie außerdem sicher, dass das Skript genügend Zeit für die Ausführung hat und nicht vom Webserver auf das Ausführungszeitlimit getötet wird –

Antwort

6

curl bemerkt wahrscheinlich, und curl_error() meldet es wahrscheinlich (als CURLE_PARTIAL_FILE-Fehler), aber Ihr Code ignoriert diesen Fehler vollständig. statt

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

versuchen

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

jetzt sollten Sie einen richtigen Fehler, wenn die curl Download fehlgeschlagen. aber um Ihnen etwas zu debuggen, schlage ich vor, auch ein protected $curldebugfileh; Klasse ftps und in __construct Zugabe tun: curl_setopt_array($this->curlhandle,array(CURLOPT_VERBOSE=>true,CURLOPT_STDERR=>($this->curldebugfileh=tmpfile())));

und dann die Ausnahme ändern:

  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'])); 

und in dem __destruct: fclose($this->curldebugfileh);

jetzt sollten Sie ein ausführliches Protokoll in der Ausnahme über das, was bis zu dem beschädigten Download passiert, die wahrscheinlich, warum der Download beschädigt wurde, erhalten.

edit: nachdem ich näher gelesen habe, sehe ich, dass du kein __destruction hast, und niemals den curl handle schließt und dadurch Speicher verliert. Das solltest du wahrscheinlich auch reparieren. Das Hinzufügen von function __destruct(){curl_close($this->curlhandle);fclose($this->curldebugfileh);} würde diesen Speicher-/Ressourcenleck verhindern.

bearbeiten 2: ich sehe, du machst $fp = fopen($local, 'w') - nicht verwenden w, das wird so gut wie alles, was Sie herunterladen, auf bestimmten Betriebssystemen, wie Microsoft Windows beschädigt (es tut nicht schaden auf Linux, obwohl ..) Verwenden Sie wb, und Ihr Code ist sicher auf Windows (und anderen Betriebssystemen, einschließlich Pre-OSX Mac, DOS, CP/M, OS/2, Symbian und wahrscheinlich anderen)

edit 3: du bist auch Ressourcen auslecken, weil Sie nie schließen ($ fp); Das solltest du wahrscheinlich auch reparieren.