ssl — FTPS с PHP Curl, получая частичную загрузку

У меня проблема с получением файла с использованием php curl через ftps с неявным ssl (как обсуждалось здесь: ftp_ssl_connect с неявным ftp через tls). Проблема в том, что иногда — вероятно, 5% времени, я заканчиваю с частичной загрузкой.

Мой класс более или менее адаптирован из адаптированного из ответа Нико Вестердейла, и вот соответствующие методы:

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

Я использую это так:

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

Как я уже говорил выше, это работает почти безупречно Кроме примерно в 5% случаев результат является частично загруженным файлом. Затем я проверяю удаленный сервер и могу убедиться, что файл действительно существует целиком — попробуйте сценарий еще раз, и он неизменно получит весь файл со второй попытки.

Что может вызвать прерывистую проблему с использованием curl, как это? Моим следующим шагом будет внедрение какой-то контрольной суммы и продолжение попыток загрузки до тех пор, пока все не будет хэшировано, но это больше похоже на неаккуратный обходной путь, чем на истинное решение, и было бы неплохо узнать истинный источник проблемы.

3

Решение

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 {

Теперь вы должны получить правильную ошибку, если загрузка локона не удалась. Тем не менее, чтобы дать вам что-то для отладки, я также предлагаю добавить protected $curldebugfileh;
Класс 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);

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

edit: после прочтения я вижу, что у вас нет __destruct, и вы никогда не закрываете ручку curl, и, таким образом, происходит утечка памяти. Вы, вероятно, должны исправить это тоже. добавление function __destruct(){curl_close($this->curlhandle);fclose($this->curldebugfileh);} предотвратит утечку памяти / ресурсов.

редактировать 2: я вижу, что вы делаете $fp = fopen($local, 'w') — не используйте w, это повредит практически все, что вы загружаете, в определенных ОС, например, в Microsoft Windows (хотя это не повредит Linux), используйте wbи ваш код безопасен для запуска в Windows (и других ОС, включая Mac OS до DOS, CP / M, OS / 2, Symbian и, возможно, другие)

редактировать 3: вы также теряете ресурсы, потому что вы никогда не fclose ($ fp); Вы, вероятно, должны исправить это тоже.

7

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]