Запрос на ввод данных PHP multipart form?

Я пишу RESTful API. У меня проблемы с загрузкой изображений с использованием разных глаголов.

считаем:

у меня есть объект, который можно создать/изменить/удалить/просмотреть через post/put/delete/get запрос на URL. Запрос является формой нескольких частей, когда есть файл для загрузки, или application / xml, когда есть только текст для обработки.

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

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

основная проблема здесь заключается в том, что при попытке обработать запрос put, очевидно, $_POST не содержит данных put (насколько я могу судить!).

Для справки вот как я строю запросы:

curl -F userfile=@./image.png -F xml="<xml><object>stuff to edit</object></xml>" 
  http://example.com/object -X PUT

есть ли у кого-нибудь идеи, как я могу получить доступ к xml переменная в моем запросе PUT?

3 ответов


прежде всего,$_FILES не заполняется при обработке запросов PUT. Он заполняется только PHP при обработке POST-запросов.

вам нужно разобрать его вручную. Это относится и к" обычным " полям:

// 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 массив будет заполнен вашими параметрами, а $headers массив будет заполнен заголовками для каждой части (например:Content-Type, etc.), и $filename будет содержать исходное имя файла, если поставлены в запрос и применим к полю.

обратите внимание, что выше будет работать только для multipart типы контента. Обязательно проверьте запрос Content-Type заголовок перед использованием выше для анализа тела.


пожалуйста, не удаляйте это снова, это полезно для большинства людей, приходящих сюда! Все предыдущие ответы были частичными ответами, которые не охватывают решение, поскольку большинство людей, задающих этот вопрос, Хотели бы.

Это берет то, что было сказано выше, и дополнительно обрабатывает несколько загрузок файлов и помещает их в $_FILES, как и следовало ожидать. Чтобы заставить это работать, вы должны добавить ' Script PUT / put.php ' для вашего виртуального хоста для проекта документация. Я также подозреваю, что мне придется настроить cron для очистки любого".ТМП файлы.

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

цитирование ответа netcoder: "обратите внимание, что выше будет работать только для составных типов контента"

для работы с любым типом контента я добавил следующие строки в решение г-на 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();
    ............
    ...............