2012-11-01 25 views
6

Używam cUrl w PHP do żądania z jakiejś usługi zewnętrznej.Czytanie danych POST w PHP z cUrl

Co ciekawe, serwer odpowiada surowym "wieloczęściowym/formularzem danych" zamiast danych binarnych.

Moja strona korzysta z hostingu współdzielonego, dlatego też opcja PECL HTTP nie jest opcją.

Czy istnieje sposób na sparsowanie tych danych za pomocą PHP?

Kod próbki:

$response = curl_exec($cUrl); 

/* $response is raw "multipart/form-data" string 

    --MIMEBoundaryurn_uuid_DDF2A2C71485B8C94C135176149950475371 
    Content-Type: application/xop+xml; charset=utf-8; type="text/xml" 
    Content-Transfer-Encoding: binary 

    (xml data goes here) 

    --MIMEBoundaryurn_uuid_DDF2A2C71485B8C94C135176149950475371 
    Content-Type: application/zip 
    Content-Transfer-Encoding: binary 

    (binary file data goes here) 

*/ 

Edycja: próbuje potokiem odpowiedź na żądanie HTTP localhost, ale dane Reakcja może przekroczyć dopuszczalny rozmiar pamięci w procesie PHP. Wydłużenie limitu pamięci nie jest zbyt praktyczne, to również dramatycznie znacznie obniża wydajność serwera.

Jeśli nie ma alternatyw dla pierwotnego pytania, możesz zaproponować sposób obsługi bardzo dużych żądań POST, wraz z analizą XML, pod względem strumieni w PHP.

Wiem, że to będzie trudne, proszę komentarz. Jestem otwarty na dyskusje.

+1

Być może można użyć biblioteki parsowania poczty MIME. Zobacz http://stackoverflow.com/questions/1238642/how-to-extract-mail-atachment-with-php/1240048#1240048 – Barmar

+0

To jest czysta 'form-data' i nie zawiera nagłówków poczty, ale ja Spróbuj. Widzę to bardzo blisko rozwiązania, zanim spróbuję. – Vicary

+0

Sklasyfikowany jako komentarz, ponieważ nagroda zakończy się przed końcem nowego roku księżycowego, zanim będę mógł uzyskać kod w biurze. – Vicary

Odpowiedz

4

jeśli potrzebujesz pliku zip z odpowiedzi Myślę, że mógłbyś po prostu napisać plik tmp, aby zapisać odpowiedź na curla, i przesłać to jako obejście: Nigdy nie próbowałem tego z wieloczęściowymi lokami, ale myślę, że to powinno działać .

$fh = fopen('/tmp/foo', 'w'); 
$cUrl = curl_init('http://example.com/foo'); 
curl_setopt($cUrl, CURLOPT_FILE, $fh); // redirect output to filehandle 
curl_exec($cUrl); 
curl_close($cUrl); 
fclose($fh); // close filehandle or the file will be corrupted 

jeśli nie trzeba niczego oprócz części xml odpowiedzi możesz wyłączyć nagłówki

curl_setopt($cUrl, CURLOPT_HEADER, FALSE); 

i dodać opcję tylko zaakceptować xml jako odpowiedź

curl_setopt($cUrl, CURLOPT_HTTPHEADER, array('Accept: application/xml')); 
//That's a workaround since there is no available curl option to do so but http allows that 

[EDYT]

Strzał w ciemność ... możesz sprawdzić za pomocą tych Ustawienia curlopt aby sprawdzić, czy to pomoże modyfikujących nic

$headers = array (
    'Content-Type: multipart/form-data; boundary=' . $boundary, 
    'Content-Length: ' . strlen($requestBody), 
    'X-EBAY-API-COMPATIBILITY-LEVEL: ' . $compatLevel, // API version 
    'X-EBAY-API-DEV-NAME: ' . $devID, 
    'X-EBAY-API-APP-NAME: ' . $appID, 
    'X-EBAY-API-CERT-NAME: ' . $certID, 
    'X-EBAY-API-CALL-NAME: ' . $verb, 
    'X-EBAY-API-SITEID: ' . $siteID, 
    ); 

$cUrl = curl_init(); 
curl_setopt($cUrl, CURLOPT_URL, $serverUrl); 
curl_setopt($cUrl, CURLOPT_TIMEOUT, 30); 
curl_setopt($cUrl, CURLOPT_SSL_VERIFYPEER, 0); 
curl_setopt($cUrl, CURLOPT_SSL_VERIFYHOST, 0); 
curl_setopt($cUrl, CURLOPT_HTTPHEADER, $headers); 
curl_setopt($cUrl, CURLOPT_POST, 1); 
curl_setopt($cUrl, CURLOPT_POSTFIELDS, $requestBody); 
curl_setopt($cUrl, CURLOPT_RETURNTRANSFER, 1); 
curl_setopt($cUrl, CURLOPT_FAILONERROR, 0); 
curl_setopt($cUrl, CURLOPT_FOLLOWLOCATION, 1); 
curl_setopt($cUrl, CURLOPT_HEADER, 0); 
curl_setopt($cUrl, CURLOPT_USERAGENT, 'ebatns;xmlstyle;1.0'); 
curl_setopt($cUrl, CURLOPT_HTTP_VERSION, 1);  // HTTP version must be 1.0 
$response = curl_exec($cUrl); 

if (!$response) { 
    print "curl error " . curl_errno($cUrl) . PHP_EOL; 
} 
curl_close($cUrl); 

[EDIT II]

to tylko próba, jak wspomniano nie mogę dostać moje pofałdowany stron do reagowania z wieloczęściowych danych formularzy.Więc być delikatny ze mną tutaj;)

$content_type = ""; //use last know content-type as a trigger 
$tmp_cnt_file = "tmp/tmpfile"; 
$xml_response = ""; // this will hold the "usable" curl response 
$hidx = 0; //header index.. counting the number of different headers received 

function read_header($cUrl, $string)// this will be called once for every line of each header received 
{ 
    global $content_type, $hidx; 
    $length = strlen($string); 
    if (preg_match('/Content-Type:(.*)/', $string, $match)) 
    { 
     $content_type = $match[1]; 
     $hidx++; 
    } 
    /* 
    should set $content_type to 'application/xop+xml; charset=utf-8; type="text/xml"' for the first 
    and to 'application/zip' for the second response body 

    echo "Header: $string<br />\n"; 
    */ 
    return $length; 
} 

function read_body($cUrl, $string) 
{ 
    global $content_header, $xml_response, $tmp_cnt_file, $hidx; 
    $length = strlen($string); 
    if(stripos ($content_type , "xml") !== false) 
     $xml_response .= $string; 
    elseif(stripos ($content_type, "zip") !== false) 
    { 
     $handle = fopen($tmp_cnt_file."-".$hidx.".zip", "a"); 
     fwrite($handle, $string); 
     fclose($handle); 
    } 
    /* 
    elseif {...} else{...} 
    depending on your needs 

    echo "Received $length bytes<br />\n"; 
    */ 
    return $length; 
} 

i oczywiście ustawić odpowiednie curlopts

// Set callback function for header 
curl_setopt($cUrl, CURLOPT_HEADERFUNCTION, 'read_header'); 
// Set callback function for body 
curl_setopt($cUrl, CURLOPT_WRITEFUNCTION, 'read_body'); 

nie zapomnij, aby nie zapisywać wywinięcia odpowiedź do zmiennej ze względu na problemy z pamięcią, nadzieją wszystko, czego potrzebujesz, znajdzie się w powyższej odpowiedzi $ xml_response.

//$response = curl_exec($cUrl); 
curl_exec($cUrl); 

A dla analizowania kodu można odwołać się do $xml_response i pliki tymczasowe utworzone zaczynając tmp/tmpfile-2 w tym scenariuszu. Ponownie, nie byłem w stanie przetestować powyższego kodu w żaden sposób. Więc może to nie działa (ale powinien imho;))

[EDIT III]

że chcemy curl napisać wszystkie dane przychodzące bezpośrednio do innego (wychodzącej) strumienia, w tym przypadku połączenia gniazda

nie jestem pewien, czy to jest tak łatwe, jak to:

$fs = fsockopen($host, $port, $errno, $errstr); 
$cUrl = curl_init('http://example.com/foo'); 
curl_setopt($cUrl, CURLOPT_FILE, $fs); // redirect output to sockethandle 
curl_exec($cUrl); 
curl_close($cUrl); 
fclose($fs); // close handle 

jeszcze będziemy musieli wykorzystać nasze znane funkcje zapisu i nagłówków z tylko trochę sztuczki

//first open the socket (before initiating curl) 
$fs = fsockopen($host, $port, $errno, $errstr); 
// now for the new callback function 
function socket_pipe($cUrl, $string) 
{ 
    global $fs; 
    $length = strlen($string); 
    fputs($fs, $string); // add NOTHING to the received line just send it to $fs; that was easy wasn't it? 
    return $length; 
} 
// and of course for the CURLOPT part 
// Set callback function for header 
curl_setopt($cUrl, CURLOPT_HEADERFUNCTION, 'socket_pipe'); 
// Set the same callback function for body 
curl_setopt($cUrl, CURLOPT_WRITEFUNCTION, 'socket_pipe'); 

// do not forget to 
fclose($fs); //when we're done 

Chodzi o to, że nie edytowanie wyniku i po prostu podłączenie go do $fs spowoduje, że apache będzie nasłuchiwał na określonym porcie, do którego następnie przypisałeś swój skrypt. Albo trzeba będzie dodać jeden nagłówek wiersza, bezpośrednio po fsockopen

fputs($fp, "POST $path HTTP/1.0\n"); //where path is your script of course 
+0

To, co otrzymałem od pierwszego polecenia zwijania, jest w formacie 'multipart/form-data', który jest rzadko używany w odpowiedzi, wysyłając go jako plik bezpośrednio wstawi dane formularza do' $ _FILES' w następnym żądaniu i PHP nie zrobi nic w analizowaniu ładunku. – Vicary

+0

, więc '$ _FILES' i' $ _POST' są przechowywane w tym samym pliku, oakay. Ale nadal powinno to zapobiec przekroczeniu limitu pamięci php podczas analizowania tego pliku zamiast odpowiedzi curl przy ponownym otwieraniu pliku za pomocą fopen i analizowania go z obsługi plików. Możesz chcieć odczytać rozmiar nagłówka i rozmiary ciała z odpowiedzi i zapisać go w zmiennej, aby móc łatwo przejść do właściwego przesunięcia pliku odpowiedzi. Jeśli znajdę dzisiejszy czas, opublikuję pełny przykład. – itsid

+0

Okay ... to jest dziwne, bardzo się starałem, aby uzyskać 'wieloczęściowe/formularzowe dane' z żądania zwijania, ale nie mogę. Nawet jeśli zwijam upload-form.html zamiast upload.php, do którego wskazuje działanie formularza. Przepraszam, czy mógłbyś podać adres URL swojego "CURLOPT_URL", aby to osiągnąć? – itsid

0

Przykro mi, nie mogę pomóc, ponieważ nie napisałeś dużo kodu, ale pamiętam, że miałem podobny problem, gdy grałem z opcjami curl_setopt.

Czy używasz CURLOPT_BINARYTRANSFER?

Z dokumentacji php ->CURLOPT_BINARYTRANSFER -> TRUE, aby zwrócić surowe wyjście, gdy używany jest CURLOPT_RETURNTRANSFER.

+0

Sprawa jest taka, że ​​otrzymałem odpowiedź w formacie raw multipart/form-data, która zwykle jest używana tylko w przypadku żądań. Nie chcę, żeby był surowy, ale jakiś mechanizm parsujący apache i PHP robi, zanim coś dostanie się do mojego kodu. – Vicary

0

wystarczy ustawić CURLOPT_RETURNTRANSFER CURLOPT_POST

 $c = curl_init($url); 
     curl_setopt($c, CURLOPT_RETURNTRANSFER, true); 
     curl_setopt($c, CURLOPT_CONNECTTIMEOUT, 1); 
     curl_setopt($c, CURLOPT_TIMEOUT, 1); 
     curl_setopt($c, CURLOPT_POST, 1); 
     curl_setopt($c, CURLOPT_POSTFIELDS, 
        array()); 
     $rst_str = curl_exec($c); 
     curl_close($c); 
0

Możesz ponownie zmontować wam dane binarne robi coś takiego, mam nadzieję, że to pomaga.

$file_array = explode("\n\r", $file, 2); 
$header_array = explode("\n", $file_array[0]); 
foreach($header_array as $header_value) { 
    $header_pieces = explode(':', $header_value); 
    if(count($header_pieces) == 2) { 
    $headers[$header_pieces[0]] = trim($header_pieces[1]); 
    } 
} 
header('Content-type: ' . $headers['Content-Type']); 
header('Content-Disposition: ' . $headers['Content-Disposition']); 
echo substr($file_array[1], 1); 
0

Jeśli nie potrzebujesz danych binarnych, czy próbowałeś poniżej?

curl_setopt($c, CURLOPT_NOBODY, true);