2012-02-27 41 views
11

Tôi đang viết một API RESTful. Tôi gặp sự cố khi tải lên hình ảnh bằng các động từ khác nhau.Yêu cầu PUT của dữ liệu nhiều phần PHP?

xem xét:

tôi có một đối tượng có thể được tạo/chỉnh sửa/xóa/xem qua một bài/đặt/xóa/được yêu cầu đến một URL. Yêu cầu là hình thức đa phần khi có tệp để tải lên hoặc ứng dụng/xml khi chỉ có văn bản để xử lý.

Để xử lý các tải lên hình ảnh đó có liên quan đến đối tượng tôi đang làm một cái gì đó như:

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

Vấn đề chính ở đây là khi cố gắng xử lý một yêu cầu đặt, rõ ràng là $ _POST không chứa đặt dữ liệu (theo như tôi có thể nói!).

Để tham khảo đây là cách tôi đang xây dựng các yêu cầu:

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

Có ai có bất kỳ ý tưởng làm thế nào tôi có thể truy cập các biến xml trong yêu cầu PUT của tôi?

Trả lời

24

Trước hết, $_FILES không được điền khi xử lý yêu cầu PUT. Nó chỉ được phổ biến bởi PHP khi xử lý các yêu cầu POST.

Bạn cần phân tích cú pháp theo cách thủ công. Mà đi cho các lĩnh vực "thường xuyên" cũng như:

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

} 

Tại mỗi lần lặp, mảng $data sẽ được áp dụng với các thông số của bạn, và các mảng $headers sẽ được áp dụng với các tiêu đề cho từng phần (ví dụ: Content-Type, vv .) và $filename sẽ chứa tên tệp gốc, nếu được cung cấp trong yêu cầu và có thể áp dụng cho trường.

Lưu ý rằng ở trên sẽ chỉ hoạt động cho multipart loại nội dung. Đảm bảo kiểm tra yêu cầu Content-Type tiêu đề trước khi sử dụng phần trên để phân tích cú pháp nội dung.

+0

Cảm ơn, điều đó sẽ hữu ích hơn :) – Josh

+1

"Trước hết, $ _FILES không được điền khi xử lý yêu cầu PUT. Nó chỉ được điền bởi PHP khi xử lý yêu cầu POST". Không thể tìm thấy tài liệu về điều này, bạn có thể vui lòng chỉ cho tôi đúng hướng không? – WDRust

+1

@ M.Ang .: [Ở đây] (http://php.net/manual/en/features.file-upload.post-method.php): "PHP cũng hỗ trợ tải lên tệp phương thức PUT như được sử dụng bởi trình soạn thảo Netscape Composer và máy khách Amaya của W3C. Xem [Hỗ trợ phương pháp PUT] (http://php.net/manual/en/features.file-upload.put- method.php) để biết thêm chi tiết. " – netcoder

0

Trích dẫn bài trả lời netcoder: "Hãy lưu ý ở trên sẽ chỉ làm việc với nhiều loại nội dung nhiều phần dữ liệu"

Để làm việc với bất kỳ loại nội dung tôi đã thêm các dòng sau đây để giải pháp của ông netcoder:

// 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 Tôi không có quyền chỉnh sửa tại thời điểm tôi thêm giải pháp. và nếu tôi thêm mã của tôi làm nhận xét trong chuỗi của trình duyệt netcoder, thì mã sẽ không thể đọc được. Tại sao -1? Tôi không cố giúp ai đó sao? – sudip

8

Vui lòng không xóa lại điều này, điều này sẽ hữu ích cho phần lớn những người đến đây! Tất cả các câu trả lời trước đó là một phần câu trả lời không bao gồm giải pháp vì phần lớn những người hỏi câu hỏi này sẽ muốn.

Điều này nhận những gì đã được nói ở trên và bổ sung xử lý nhiều tệp tải lên và đặt chúng ở $ _FILES như một người mong đợi. Để làm việc này, bạn phải thêm 'Script PUT /put.php' vào Máy chủ ảo của mình cho dự án theo Documentation. Tôi cũng nghi ngờ tôi sẽ phải thiết lập một cron để dọn dẹp bất kỳ tập tin '.tmp'.

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

Nó thực sự trông như thế này là câu trả lời duy nhất mà đi bất cứ nơi nào gần một giải pháp hoàn chỉnh. Tôi không chắc chắn làm thế nào tôi cảm thấy về giả mạo '$ _FILES' và' $ _PUT' nhưng nó hoạt động thực sự tốt. Cảm ơn! –

+0

đó chỉ là những gì tôi cần! –

+0

Làm thế nào tôi có thể đặt điều này để làm việc trong một yêu cầu Laravel? "Chiếu sáng \ Http \ Yêu cầu" –

Các vấn đề liên quan