Запрос на ввод данных 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();
............
...............