2012-02-27 6 views
11

RESTful API'sı yazıyorum. Farklı fiilleri kullanarak görüntü yüklemeyle ilgili sorun yaşıyorum.PHP çok parçalı form verileri PUT talebi?

düşünün:

ben oluşturulabilir bir nesneyi/modifiye/silinmiş/bir URL'ye bir post/satım/get/silme isteği ile görüntülenebilir var. Yüklenecek bir dosya olduğunda ya da yalnızca işlenecek metin olduğunda uygulama/xml olduğunda istek çok bölümlü formdur.

Ben böyle bir şey yapıyorum nesne ile ilişkili olan görüntü yüklenenler işlemek için:

if(isset($_FILES['userfile'])) { 
     $data = $this->image_model->upload_image(); 
     if($data['error']){ 
      $this->response(array('error' => $error['error'])); 
     } 
     $xml_data = (array)simplexml_load_string(urldecode($_POST['xml']));   
     $object = (array)$xml_data['object']; 
    } else { 
     $object = $this->body('object'); 
    } 

burada önemli sorun bir put isteği işlemek için çalışıyor, belli ki $ _POST put içermiyor veri (anlatabildiğim kadarıyla). Başvuru için

bu benim isteklerini inşa ediyorum nasıl:

curl -F [email protected]/image.png -F xml="<xml><object>stuff to edit</object></xml>" 
    http://example.com/object -X PUT 

kimse benim PUT isteğinde xml değişkeni nasıl erişebileceğini herhangi bir fikir var mı?

cevap

24

PUT istekleri işlenirken her şeyden önce $_FILES doldurulmaz. POST istekleri işlenirken yalnızca PHP tarafından doldurulur.

El ile ayrıştırmanız gerekir. Yani hem "normal" alanlar için de geçerli:

her adımda
// Fetch content and determine boundary 
$raw_data = file_get_contents('php://input'); 
$boundary = substr($raw_data, 0, strpos($raw_data, "\r\n")); 

// Fetch each part 
$parts = array_slice(explode($boundary, $raw_data), 1); 
$data = array(); 

foreach ($parts as $part) { 
    // If this is the last part, break 
    if ($part == "--\r\n") break; 

    // Separate content from headers 
    $part = ltrim($part, "\r\n"); 
    list($raw_headers, $body) = explode("\r\n\r\n", $part, 2); 

    // Parse the headers list 
    $raw_headers = explode("\r\n", $raw_headers); 
    $headers = array(); 
    foreach ($raw_headers as $header) { 
     list($name, $value) = explode(':', $header); 
     $headers[strtolower($name)] = ltrim($value, ' '); 
    } 

    // Parse the Content-Disposition to get the field name, etc. 
    if (isset($headers['content-disposition'])) { 
     $filename = null; 
     preg_match(
      '/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/', 
      $headers['content-disposition'], 
      $matches 
     ); 
     list(, $type, $name) = $matches; 
     isset($matches[4]) and $filename = $matches[4]; 

     // handle your fields here 
     switch ($name) { 
      // this is a file upload 
      case 'userfile': 
       file_put_contents($filename, $body); 
       break; 

      // default for all other files is to populate $data 
      default: 
       $data[$name] = substr($body, 0, strlen($body) - 2); 
       break; 
     } 
    } 

} 

, $data dizi sizin parametreleri ile doldurulur ve $headers dizisi (örneğin her bölümü için başlıklarıyla doldurulur: Content-Type, vs .) ve $filename, istekte sağlanmışsa ve alana uygulanabilirse orijinal dosya adını içerecektir.

Not: Yukarıdakiler yalnızca multipart içerik türleri için çalışacaktır. Vücudu ayrıştırmak için yukarıdakileri kullanmadan önce Content-Type başlığını kontrol ettiğinizden emin olun.

+0

Teşekkürler, bu yükler daha yararlı olur :) – Josh

+1

"PUT isteklerini işlerken Öncelikle $ _FILES. Doldurulur değil POST isteklerini işlerken sadece PHP tarafından doldurulur." Bunun üzerinde dokümantasyon bulunamıyor, lütfen beni doğru yöne yönlendirebilir misiniz? – WDRust

+1

@ M.Ang .: [Here] (http://php.net/manual/en/features.file-upload.post-method.php): "PHP ayrıca, Netscape Composer ve W3C'nin Amaya istemcileri tarafından kullanılan PUT yöntemi dosya yüklemelerini de destekler. [PUT Method Desteği] (http://php.net/manual/en/features.file-upload.put- method.php) daha fazla detay için. – netcoder

0

netcoder cevap alıntı: Bay netcoder çözümüyle aşağıdaki satırları eklediğiniz tüm içerik türü ile çalışmak için

"yukarıda sadece çok parçalı içerik türleri için çalışacak dikkatinizi çekeriz":

// Fetch content and determine boundary 
    $raw_data = file_get_contents('php://input'); 
    $boundary = substr($raw_data, 0, strpos($raw_data, "\r\n")); 

    /*...... My edit --------- */ 
    if(empty($boundary)){ 
     parse_str($raw_data,$data); 
     return $data; 
    } 
    /* ........... My edit ends ......... */ 
    // Fetch each part 
    $parts = array_slice(explode($boundary, $raw_data), 1); 
    $data = array(); 
    ............ 
    ............... 
+0

@Greg Çözümü eklediğim sırada düzenleme iznim yoktu. ve kodumu net kodun iş parçacığına bir yorum olarak eklerseniz, kod okunamazdı. Neden -1? Birine yardım etmeye çalışmadım mı? – sudip

8

Lütfen bunu bir daha silmeyin, buraya gelenlerin çoğuna yardımcı olur! Önceki bütün cevaplar, bu soruyu soran insanların çoğunluğu olarak çözümü kapsamayan kısmi cevaplardı.

Bu, yukarıda söylenenleri alır ve ek olarak birden fazla dosya yüklemesini işler ve birisinin beklediği gibi $ _FILES olarak yerleştirir. Bunu işe almak için, Documentation'a göre proje için Sanal Sunucunuza 'Script PUT /put.php' eklemeniz gerekir. Ayrıca, herhangi bir '.tmp' dosyasını temizlemek için bir cron kurmam gerektiğinden de şüpheleniyorum.

private function _parsePut() 
{ 
    global $_PUT; 

    /* PUT data comes in on the stdin stream */ 
    $putdata = fopen("php://input", "r"); 

    /* Open a file for writing */ 
    // $fp = fopen("myputfile.ext", "w"); 

    $raw_data = ''; 

    /* Read the data 1 KB at a time 
     and write to the file */ 
    while ($chunk = fread($putdata, 1024)) 
     $raw_data .= $chunk; 

    /* Close the streams */ 
    fclose($putdata); 

    // Fetch content and determine boundary 
    $boundary = substr($raw_data, 0, strpos($raw_data, "\r\n")); 

    if(empty($boundary)){ 
     parse_str($raw_data,$data); 
     $GLOBALS[ '_PUT' ] = $data; 
     return; 
    } 

    // Fetch each part 
    $parts = array_slice(explode($boundary, $raw_data), 1); 
    $data = array(); 

    foreach ($parts as $part) { 
     // If this is the last part, break 
     if ($part == "--\r\n") break; 

     // Separate content from headers 
     $part = ltrim($part, "\r\n"); 
     list($raw_headers, $body) = explode("\r\n\r\n", $part, 2); 

     // Parse the headers list 
     $raw_headers = explode("\r\n", $raw_headers); 
     $headers = array(); 
     foreach ($raw_headers as $header) { 
      list($name, $value) = explode(':', $header); 
      $headers[strtolower($name)] = ltrim($value, ' '); 
     } 

     // Parse the Content-Disposition to get the field name, etc. 
     if (isset($headers['content-disposition'])) { 
      $filename = null; 
      $tmp_name = null; 
      preg_match(
       '/^(.+); *name="([^"]+)"(; *filename="([^"]+)")?/', 
       $headers['content-disposition'], 
       $matches 
      ); 
      list(, $type, $name) = $matches; 

      //Parse File 
      if(isset($matches[4])) 
      { 
       //if labeled the same as previous, skip 
       if(isset($_FILES[ $matches[ 2 ] ])) 
       { 
        continue; 
       } 

       //get filename 
       $filename = $matches[4]; 

       //get tmp name 
       $filename_parts = pathinfo($filename); 
       $tmp_name = tempnam(ini_get('upload_tmp_dir'), $filename_parts['filename']); 

       //populate $_FILES with information, size may be off in multibyte situation 
       $_FILES[ $matches[ 2 ] ] = array(
        'error'=>0, 
        'name'=>$filename, 
        'tmp_name'=>$tmp_name, 
        'size'=>strlen($body), 
        'type'=>$value 
       ); 

       //place in temporary directory 
       file_put_contents($tmp_name, $body); 
      } 
      //Parse Field 
      else 
      { 
       $data[$name] = substr($body, 0, strlen($body) - 2); 
      } 
     } 

    } 
    $GLOBALS[ '_PUT' ] = $data; 
    return; 
} 
+0

Gerçekten de bunun tam bir çözümün yakınındaki herhangi bir yere giden tek cevap olduğu anlaşılıyor. '$ _FILES' ve' $ _PUT' hakkında ne yazdığımı bilmiyorum ama gerçekten iyi çalışıyor. Teşekkürler! –

+0

sadece ihtiyacım olan şey bu! –

+0

Bunu Laravel İsteğinde çalışmak için nasıl kullanabilirim? "Aydınlatmak \ Http \ İsteği" –