Я пишу 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?
Во-первых, $_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
и т. Д.), А $filename
будет содержать исходное имя файла, если поставляемый в запросе и применимый к полю.
Обратите внимание, что приведенное выше будет работать только для multipart
типов контента. Обязательно проверьте заголовок Content-Type
запроса перед использованием выше, чтобы проанализировать тело.
Пожалуйста, не удаляйте это снова, это полезно для большинства людей, приезжающих сюда! Все предыдущие ответы были частичными ответами, которые не охватывают решение, поскольку большинство людей, задающих этот вопрос, захотят.
Это берет то, что было сказано выше, и дополнительно обрабатывает несколько загрузок файлов и помещает их в $ _FILES, как и ожидалось. Чтобы это сработало, вам нужно добавить «Script PUT /put.php» на ваш виртуальный хост для проекта на документацию . Я также подозреваю, что мне придется настроить cron для очистки любых файлов .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; }
Цитирование ответа 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(); ............ ...............