Как использовать PHP curl_multi_init(), когда мне нужно передать пользовательские параметры?

#php #curl #remote-server

#php #curl #удаленный сервер

Вопрос:

У меня есть пользовательская функция cURL, которая должна загружать огромное количество изображений с удаленного сервера. Меня забанили пару раз раньше, когда я использовал file_get_contents() . Я обнаружил, что curl_multi_init() — лучший вариант, так как при 1 подключении он может загружать, например, 20 изображений одновременно. Я создал пользовательские функции, которые используют curl_init(), и я пытаюсь выяснить, как я могу реализовать curl_multi_init(), поэтому в моем ЦИКЛЕ, где я беру список из 20 URL-адресов из базы данных, я могу вызвать свою пользовательскую функцию и в последнем цикле использовать curl_close() . В текущей ситуации моя функция генерирует соединение для каждого URL-адреса в ЦИКЛЕ. Вот функция:

 function downloadUrlToFile($remoteurl,$newfileName){
$errors = 0;
    $options = array(
      CURLOPT_FILE    => fopen('../images/products/'.$newfileName, 'w'),
      CURLOPT_TIMEOUT =>  28800,
      CURLOPT_URL     => $remoteurl,
      CURLOPT_RETURNTRANSFER => 1
    );

    $ch = curl_init();
    curl_setopt_array($ch, $options);
    $imageString =curl_exec($ch);
    $image = imagecreatefromstring($imageString);
    if($imageString !== false AND !empty($imageString)){
    if ($image !== false){
        $width_orig = imagesx($image);
        if($width_orig > 1000){
        $saveimage = copy_and_resize_remote_image_product($image,$newfileName);
        }else $saveimage = file_put_contents('../images/products/'.$newfileName,$imageString);
        
        }else $errors  ;
    }else $errors  ;
    curl_close($ch);
    return $errors;
}
  

Должен быть способ использовать curl_multi_init() и мою функцию downloadUrlToFile, потому что:

  1. Мне нужно изменить имя файла на лету
  2. В моей функции я также проверяю несколько вещей для удаленного изображения.. В примере функции я проверяю только размер и изменяю его размер, если это необходимо, но эта функция выполняет гораздо больше задач (я сократил эту часть для сокращения, но я также использую функцию для передачи большего количества переменных ..)

Как следует изменить код, чтобы во время цикла подключаться к удаленному серверу только один раз?

Заранее спасибо

Ответ №1:

Попробуйте этот шаблон для Multi CURL

 $urls = array($url_1, $url_2, $url_3);
$content = array();

$ch = array();
$mh = curl_multi_init();

foreach( $urls as $index => $url ) {
    $ch[$index] = curl_init();
    curl_setopt($ch[$index], CURLOPT_URL, $url);
    curl_setopt($ch[$index], CURLOPT_HEADER, 0);
    curl_setopt($ch[$index], CURLOPT_RETURNTRANSFER, true);
    curl_multi_add_handle($mh, $ch[$index]);
}

$active = null;
for(;;) {
    curl_multi_exec($mh, $active);
    if($active < 1){
        // all downloads completed
        break;
    }else{
        // sleep-wait for more data to arrive on socket.
        // (without this, we would be wasting 100% cpu of 1 core while downloading,
        // with this, we'll be using like 1-2% cpu of 1 core instead.)
        curl_multi_select($mh, 1);
    }
}

foreach ( $ch AS $index => $c ) {

    $content[$index] = curl_multi_getcontent($c);
    curl_multi_remove_handle($mh, $c);

    //You can add some functions here and use $content[$index]
}

curl_multi_close($mh);
  

Комментарии:

1. #Александр, спасибо за ваше время. Это был мой первый подход, когда я обнаружил, что с подобным кодом я не могу передать несколько переменных, которые мне нужны. Если бы я мог, этот код мог бы работать для меня, но.. Например, мне нужно передать: product_id, product_name, product_code.. В моем примере функции я показываю только название продукта, но я объяснил, что на самом деле я отправил больше переменных

2. не используйте busyloop; это приведет к потере 100% процессора (из 1 ядра процессора) без уважительной причины. вместо этого попробуйте: for(;;){curl_multi_exec($mh,$active);if($active<1){/*all downloads completed*/break;}curl_multi_select($mh,1);} — он будет использовать намного меньше процессора и не будет медленнее. (серьезно, если у вас одноядерный экземпляр AWS, ваш код будет использовать 100% cpu, пока все не будет загружено. добавьте туда select(), и он будет использовать примерно 1-2% процессора, пока все не будет загружено.)

3. #ханшенрик что такое busyloop? Был ли этот комментарий для ответа Александра?

4. @Europeuser вы можете прочитать об этом здесь , была проблема с исходным кодом Александра, где он использовал бы что-то вроде 98% больше процессора, чем на самом деле необходимо. но не обращайте внимания на это, код обновляется, эта проблема исправлена 🙂